package storage import ( "bytes" "context" "sync" "testing" ) // fakeUsageReporter zeichnet jeden Report-Aufruf auf, damit Tests // nachweisen koennen, dass Service tatsaechlich meldet (Akzeptanzkriterium // 4 / Pruefung 4) — ohne echten HTTP-Aufruf gegen Core. type fakeUsageReporter struct { mu sync.Mutex calls []reportCall failOn int // wenn >0, schlaegt der reportCall-te Aufruf fehl } type reportCall struct { tenantSlug string metric string delta int64 } func (f *fakeUsageReporter) Report(ctx context.Context, tenantSlug, metric string, delta int64) error { f.mu.Lock() defer f.mu.Unlock() f.calls = append(f.calls, reportCall{tenantSlug, metric, delta}) if f.failOn > 0 && len(f.calls) == f.failOn { return context.DeadlineExceeded } return nil } // TestService_PutReportsPositiveDelta ist Pruefung 4 (Schreibvorgang): // Melde-Aufruf an Core wird bei Put ausgeloest, mit korrekter Groesse. func TestService_PutReportsPositiveDelta(t *testing.T) { driver := NewLocalDriver(t.TempDir(), []byte("secret"), "https://files.example.test") usage := &fakeUsageReporter{} svc := NewService(driver, usage, "acme") content := []byte("zwoelf bytes") if _, err := svc.Put(context.Background(), "documents/d1/revisions/r1", bytes.NewReader(content), int64(len(content)), "text/plain"); err != nil { t.Fatalf("put: %v", err) } if len(usage.calls) != 1 { t.Fatalf("erwartet 1 nutzungsmeldung, habe %d", len(usage.calls)) } call := usage.calls[0] if call.tenantSlug != "acme" || call.metric != UsageMetric || call.delta != int64(len(content)) { t.Fatalf("unerwarteter meldungsinhalt: %+v", call) } } // TestService_DeleteReportsNegativeDelta ist Pruefung 4 (Loeschvorgang). func TestService_DeleteReportsNegativeDelta(t *testing.T) { driver := NewLocalDriver(t.TempDir(), []byte("secret"), "https://files.example.test") usage := &fakeUsageReporter{} svc := NewService(driver, usage, "acme") ctx := context.Background() key := "documents/d2/revisions/r1" if _, err := svc.Put(ctx, key, bytes.NewReader([]byte("abc")), 3, "text/plain"); err != nil { t.Fatalf("put: %v", err) } if err := svc.Delete(ctx, key, 3); err != nil { t.Fatalf("delete: %v", err) } if len(usage.calls) != 2 { t.Fatalf("erwartet 2 nutzungsmeldungen (put+delete), habe %d", len(usage.calls)) } del := usage.calls[1] if del.delta != -3 { t.Fatalf("delete-delta = %d, want -3", del.delta) } } // TestService_GetMissingObjectReturnsClearError ist Pruefung 3 auf // Service-Ebene. func TestService_GetMissingObjectReturnsClearError(t *testing.T) { driver := NewLocalDriver(t.TempDir(), []byte("secret"), "https://files.example.test") svc := NewService(driver, &fakeUsageReporter{}, "acme") if _, err := svc.Get(context.Background(), "nie-angelegt"); err == nil { t.Fatal("get eines fehlenden objekts haette einen fehler liefern muessen") } }