feat(store): add DeleteUser with cascading account deletion

This commit is contained in:
2026-07-26 10:17:21 +02:00
parent 65a2dc90d0
commit e8c340a00e
5 changed files with 142 additions and 12 deletions

View File

@@ -161,3 +161,46 @@ func TestIsolation_SyncStateAndRunsNeverLeakAcrossUsers(t *testing.T) {
t.Fatal("userB's FinishSyncRun call was able to mutate userA's sync run")
}
}
// TestIsolation_DeleteUserLeavesOtherUsersDataIntact confirms deleting one
// user's account never touches another user's profile, taxonomy, or
// activities, even though DeleteUser is a single blunt DELETE FROM users.
func TestIsolation_DeleteUserLeavesOtherUsersDataIntact(t *testing.T) {
db := openTestDB(t)
ctx := context.Background()
userA, err := db.ProvisionUser(ctx, "sub-a", "A")
if err != nil {
t.Fatalf("ProvisionUser(a): %v", err)
}
userB, err := db.ProvisionUser(ctx, "sub-b", "B")
if err != nil {
t.Fatalf("ProvisionUser(b): %v", err)
}
if _, err := db.UpsertActivity(ctx, userB, Activity{GarminActivityID: 1, StartTimeUTC: "2026-07-11 06:00:00", RawJSON: "{}"}); err != nil {
t.Fatalf("UpsertActivity(b): %v", err)
}
if err := db.DeleteUser(ctx, userA); err != nil {
t.Fatalf("DeleteUser(a): %v", err)
}
if _, found, err := db.GetUserBySub(ctx, "sub-b"); err != nil || !found {
t.Fatalf("expected userB to survive userA's deletion, found=%v err=%v", found, err)
}
profileB, err := db.GetProfile(ctx, userB)
if err != nil {
t.Fatalf("GetProfile(b) after deleting a: %v", err)
}
if profileB.Name != "B" {
t.Errorf("userB's profile changed after deleting userA: %+v", profileB)
}
kindsB, err := db.ListWorkoutKinds(ctx, userB, false)
if err != nil || len(kindsB) != 8 {
t.Fatalf("userB's taxonomy affected by deleting userA: len=%d err=%v", len(kindsB), err)
}
activitiesB, err := db.ListActivities(ctx, userB, ActivityFilter{})
if err != nil || len(activitiesB) != 1 {
t.Fatalf("userB's activities affected by deleting userA: len=%d err=%v", len(activitiesB), err)
}
}