refactor: remove legacy spaces and migrate lists to standalone resources with direct sharing
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 59s

This commit is contained in:
sandrodach committed 2026-10-25 18:17:17 +01:00
1 parent 177b69234c
commit 0d6c603e9c
9 files changed
+616 -950

No files matched your search

+39 -41
View File
@@ -117,7 +117,6 @@ func TestFirstBootSetupAndInviteRegistrationAndNonNullSlices(t *testing.T) {
"/api/v1/tasks",
"/api/v1/organizations",
"/api/v1/teams",
"/api/v1/spaces",
"/api/v1/lists",
}
for _, ep := range arrayEndpoints {
@@ -190,7 +189,6 @@ func TestConcurrentInitialLoadAndListsNoDeadlock(t *testing.T) {
"/api/v1/auth/me",
"/api/v1/triage/deck",
"/api/v1/lists",
"/api/v1/spaces",
"/api/v1/tasks",
"/api/v1/documents",
}
@@ -557,104 +555,104 @@ func TestSpaceMembersDirectInvitationAPI(t *testing.T) {
t.Fatalf("failed to generate token: %v", err)
}
// 1. Admin creates a space
createSpaceBody := []byte(`{"name":"Einkauf WG","icon":"🛒","description":"Gemeinsame Einkaufsliste"}`)
req := httptest.NewRequest(http.MethodPost, "/api/v1/spaces", bytes.NewReader(createSpaceBody))
// 1. Admin creates a list
createListBody := []byte(`{"title":"Einkauf WG","icon":"🛒","listType":"shopping"}`)
req := httptest.NewRequest(http.MethodPost, "/api/v1/lists", bytes.NewReader(createListBody))
req.Header.Set("Authorization", "Bearer "+adminToken)
req.Header.Set("Content-Type", "application/json")
rec := httptest.NewRecorder()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusCreated {
t.Fatalf("expected 201 Created from /api/v1/spaces, got %d: %s", rec.Code, rec.Body.String())
t.Fatalf("expected 201 Created from /api/v1/lists, got %d: %s", rec.Code, rec.Body.String())
}
var createdSpace domain.Space
_ = json.Unmarshal(rec.Body.Bytes(), &createdSpace)
if createdSpace.ID == "" {
t.Fatalf("expected created space with ID, got %+v", createdSpace)
var createdList domain.SharedList
_ = json.Unmarshal(rec.Body.Bytes(), &createdList)
if createdList.ID == "" {
t.Fatalf("expected created list with ID, got %+v", createdList)
}
// 2. Admin invites normalUser by username "user"
addMemberBody := []byte(`{"userId":"user","role":"editor"}`)
req = httptest.NewRequest(http.MethodPost, "/api/v1/spaces/"+createdSpace.ID+"/members", bytes.NewReader(addMemberBody))
// 2. Admin shares list with normalUser by username "user"
addShareBody := []byte(`{"targetType":"user","targetId":"user","role":"editor"}`)
req = httptest.NewRequest(http.MethodPost, "/api/v1/lists/"+createdList.ID+"/shares", bytes.NewReader(addShareBody))
req.Header.Set("Authorization", "Bearer "+adminToken)
req.Header.Set("Content-Type", "application/json")
rec = httptest.NewRecorder()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusCreated {
t.Fatalf("expected 201 Created from add space member, got %d: %s", rec.Code, rec.Body.String())
t.Fatalf("expected 201 Created from add list share, got %d: %s", rec.Code, rec.Body.String())
}
var newMember domain.SpaceMember
_ = json.Unmarshal(rec.Body.Bytes(), &newMember)
if newMember.UserID != normalUser.ID {
t.Fatalf("expected member userId %s, got %s", normalUser.ID, newMember.UserID)
var newShare domain.ListShare
_ = json.Unmarshal(rec.Body.Bytes(), &newShare)
if newShare.TargetID != normalUser.ID {
t.Fatalf("expected share targetId %s, got %s", normalUser.ID, newShare.TargetID)
}
// 3. User checks their spaces; the shared space must be listed
req = httptest.NewRequest(http.MethodGet, "/api/v1/spaces", nil)
// 3. User checks their lists; the shared list must be listed
req = httptest.NewRequest(http.MethodGet, "/api/v1/lists", nil)
req.Header.Set("Authorization", "Bearer "+userToken)
rec = httptest.NewRecorder()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected 200 OK for user spaces, got %d", rec.Code)
t.Fatalf("expected 200 OK for user lists, got %d", rec.Code)
}
var userSpaces []domain.Space
_ = json.Unmarshal(rec.Body.Bytes(), &userSpaces)
var userLists []domain.SharedList
_ = json.Unmarshal(rec.Body.Bytes(), &userLists)
found := false
for _, s := range userSpaces {
if s.ID == createdSpace.ID {
for _, l := range userLists {
if l.ID == createdList.ID {
found = true
break
}
}
if !found {
t.Fatalf("expected space %s to be visible to user, got %+v", createdSpace.ID, userSpaces)
t.Fatalf("expected list %s to be visible to user, got %+v", createdList.ID, userLists)
}
// 4. Admin removes user from space
req = httptest.NewRequest(http.MethodDelete, "/api/v1/spaces/"+createdSpace.ID+"/members/"+normalUser.ID, nil)
// 4. Admin removes user share from list
req = httptest.NewRequest(http.MethodDelete, "/api/v1/lists/"+createdList.ID+"/shares/"+newShare.ID, nil)
req.Header.Set("Authorization", "Bearer "+adminToken)
rec = httptest.NewRecorder()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected 200 OK for remove member, got %d: %s", rec.Code, rec.Body.String())
t.Fatalf("expected 200 OK for remove share, got %d: %s", rec.Code, rec.Body.String())
}
// 5. Test adding a team to a space
// 5. Test sharing list with a team
team, err := store.CreateTeam("", "WG Team", "Haushalt")
if err != nil {
t.Fatalf("failed to create team: %v", err)
}
_ = store.AddTeamMember(team.ID, normalUser.ID, "member")
addTeamBody := []byte(`{"teamId":"` + team.ID + `","role":"editor"}`)
req = httptest.NewRequest(http.MethodPost, "/api/v1/spaces/"+createdSpace.ID+"/members", bytes.NewReader(addTeamBody))
addTeamBody := []byte(`{"targetType":"team","targetId":"` + team.ID + `","role":"editor"}`)
req = httptest.NewRequest(http.MethodPost, "/api/v1/lists/"+createdList.ID+"/shares", bytes.NewReader(addTeamBody))
req.Header.Set("Authorization", "Bearer "+adminToken)
req.Header.Set("Content-Type", "application/json")
rec = httptest.NewRecorder()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusCreated && rec.Code != http.StatusOK {
t.Fatalf("expected 201 or 200 for add space team, got %d: %s", rec.Code, rec.Body.String())
t.Fatalf("expected 201 or 200 for add list team share, got %d: %s", rec.Code, rec.Body.String())
}
// User should see the space again because their team was added
req = httptest.NewRequest(http.MethodGet, "/api/v1/spaces", nil)
// User should see the list again because their team was added
req = httptest.NewRequest(http.MethodGet, "/api/v1/lists", nil)
req.Header.Set("Authorization", "Bearer "+userToken)
rec = httptest.NewRecorder()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected 200 OK for user spaces after team add, got %d", rec.Code)
t.Fatalf("expected 200 OK for user lists after team share add, got %d", rec.Code)
}
userSpaces = nil
_ = json.Unmarshal(rec.Body.Bytes(), &userSpaces)
userLists = nil
_ = json.Unmarshal(rec.Body.Bytes(), &userLists)
found = false
for _, s := range userSpaces {
if s.ID == createdSpace.ID {
for _, l := range userLists {
if l.ID == createdList.ID {
found = true
break
}
}
if !found {
t.Fatalf("expected space %s to be visible to user via team, got %+v", createdSpace.ID, userSpaces)
t.Fatalf("expected list %s to be visible to user via team share, got %+v", createdList.ID, userLists)
}
}