package web import ( "net/http" "net/http/httptest" "strings" "testing" "sourcecraft.dev/bigbes/sr-ht-dolt/core" "sourcecraft.dev/bigbes/sr-ht-dolt/db" ) // postInternalCreate calls handleInternalCreate directly with a JSON body, // bypassing the router (and thus internalAuthGuard, which is exercised // separately). The auth guard needs the process-global crypto/config state a // unit test does not set up; the business logic under test here does not. func (h *harness) postInternalCreate(body string) *httptest.ResponseRecorder { req := httptest.NewRequest("POST", "/internal/repos", strings.NewReader(body)) req.Header.Set("Content-Type", "application/json") rec := httptest.NewRecorder() h.app.handleInternalCreate(rec, req) return rec } func TestInternalCreateProvisions(t *testing.T) { h := newHarness(t) h.users.byName["alice"] = &core.Caller{UserID: 7, Username: "alice"} rec := h.postInternalCreate(`{"owner":"~alice","name":"widgets","description":"hi"}`) if rec.Code != http.StatusCreated { t.Fatalf("create: got %d, want 201 (body: %s)", rec.Code, rec.Body.String()) } if !strings.Contains(rec.Body.String(), `"created":true`) { t.Fatalf("expected created:true, got %s", rec.Body.String()) } if !strings.Contains(rec.Body.String(), "/~alice/widgets") { t.Fatalf("expected companion URL in body, got %s", rec.Body.String()) } // Row inserted with the resolved owner id and default (PRIVATE) visibility. if len(h.store.createdCalls) != 1 { t.Fatalf("expected 1 CreateRepo call, got %d", len(h.store.createdCalls)) } got := h.store.createdCalls[0] if got.OwnerID != 7 || got.OwnerName != "alice" || got.Name != "widgets" { t.Fatalf("unexpected repo: %+v", got) } if got.Visibility != core.VisibilityPrivate { t.Fatalf("default visibility: got %q, want PRIVATE", got.Visibility) } // On-disk store initialized at the mapped path. if len(h.stores.initCalls) != 1 || !strings.HasSuffix(h.stores.initCalls[0], "/~alice/widgets") { t.Fatalf("expected InitStore at ~alice/widgets, got %v", h.stores.initCalls) } } func TestInternalCreateIdempotent(t *testing.T) { h := newHarness(t) h.users.byName["alice"] = &core.Caller{UserID: 7, Username: "alice"} h.store.add(&core.Repo{Name: "widgets", OwnerID: 7, OwnerName: "alice", Path: "/p", Visibility: core.VisibilityPrivate}) rec := h.postInternalCreate(`{"owner":"alice","name":"widgets"}`) if rec.Code != http.StatusOK { t.Fatalf("existing companion: got %d, want 200 (body: %s)", rec.Code, rec.Body.String()) } if !strings.Contains(rec.Body.String(), `"created":false`) { t.Fatalf("expected created:false, got %s", rec.Body.String()) } // Must NOT touch disk when the row already exists. if len(h.stores.initCalls) != 0 { t.Fatalf("InitStore must not run for an existing companion, got %v", h.stores.initCalls) } } func TestInternalCreateRollsBackOnStoreFailure(t *testing.T) { h := newHarness(t) h.users.byName["alice"] = &core.Caller{UserID: 7, Username: "alice"} h.stores.initErr = errInitBoom rec := h.postInternalCreate(`{"owner":"alice","name":"widgets"}`) if rec.Code != http.StatusInternalServerError { t.Fatalf("store failure: got %d, want 500", rec.Code) } // The metadata row inserted before InitStore must be rolled back. if len(h.store.deletedRepos) != 1 { t.Fatalf("expected rollback DeleteRepo, got deletes=%v", h.store.deletedRepos) } if _, err := h.store.GetRepoByOwnerAndName(nil, "alice", "widgets"); err != db.ErrNotFound { t.Fatalf("expected repo removed after rollback, err=%v", err) } } func TestInternalCreateUnknownOwner(t *testing.T) { h := newHarness(t) // alice is not registered in fakeUsers -> LookupUser fails. rec := h.postInternalCreate(`{"owner":"alice","name":"widgets"}`) if rec.Code != http.StatusUnprocessableEntity { t.Fatalf("unknown owner: got %d, want 422 (body: %s)", rec.Code, rec.Body.String()) } if len(h.store.createdCalls) != 0 { t.Fatalf("must not create a row for an unresolvable owner") } } func TestInternalCreateRejectsBadName(t *testing.T) { h := newHarness(t) h.users.byName["alice"] = &core.Caller{UserID: 7, Username: "alice"} rec := h.postInternalCreate(`{"owner":"alice","name":".."}`) if rec.Code != http.StatusBadRequest { t.Fatalf("bad name: got %d, want 400", rec.Code) } } var errInitBoom = &boomError{} type boomError struct{} func (*boomError) Error() string { return "boom" }