Files
Agent-System-Backend/internal/store/project_projection_test.go
T
weichen 7cf8f02399 Add project lifecycle delegation and private member work
Why:
Shared project reads previously doubled as write permission, project mutations
bypassed the domain lifecycle, and coarse retrieval shared private resources.

Evidence:
SELF red-before-implementation logs cover projection, lifecycle, private work,
resource filtering, canonical retrieval scope, publication source and namespace.
SELF go test ./..., isolated PostgreSQL go test ./internal/store -count=1,
and CGO_ENABLED=0 go build ./cmd/agent-system passed; task owner owns replay/CI.

Impact:
Add optional actor-scoped projection/lifecycle capabilities; registered providers
control public lifecycle and live work. Preserve owned conversation history and
ordinary unregistered behavior. Authorize knowledge by stored ID/source, map
current host scopes through trusted service metadata, and filter artifact lists.

Refs:
Dev project-member-access implementation plan r1 Task 1; approved design r0.1;
2026-10-13 trusted publication Source hardening ruling
2026-10-13 08:43:02 +00:00

84 lines
2.4 KiB
Go

package store
import (
"errors"
"sync"
"testing"
)
func TestProjectProjectionIdentityAndOwnership(t *testing.T) {
st := openTestStore(t)
p, err := st.EnsureProject("alice", "host-stable", " Global ")
if err != nil || p.ID != "host-stable" || p.Name != "Global" || p.Owner != "alice" {
t.Fatalf("ensure: %+v %v", p, err)
}
retry, err := st.EnsureProject("alice", p.ID, "Global")
if err != nil || retry.ID != p.ID {
t.Fatalf("retry: %+v %v", retry, err)
}
if _, err = st.EnsureProject("bob", p.ID, "Global"); !errors.Is(err, ErrNotFound) {
t.Fatalf("other owner: %v", err)
}
if _, err = st.EnsureProject("alice", p.ID, "Different"); !errors.Is(err, ErrConflict) {
t.Fatalf("different retry name: %v", err)
}
if _, err = st.RenameOwnedProject("bob", p.ID, "Other"); !errors.Is(err, ErrNotFound) {
t.Fatalf("rename owner: %v", err)
}
}
func TestProjectProjectionGlobalNameAdmission(t *testing.T) {
st := openTestStore(t)
if _, err := st.CreateProject("legacy", "Taken"); err != nil {
t.Fatal(err)
}
if _, err := st.EnsureProject("alice", "a", " Taken "); !errors.Is(err, ErrConflict) {
t.Fatalf("legacy collision: %v", err)
}
var wg sync.WaitGroup
results := make(chan error, 2)
for _, owner := range []string{"alice", "bob"} {
wg.Add(1)
go func(owner string) {
defer wg.Done()
_, err := st.EnsureProject(owner, "p-"+owner, "Race")
results <- err
}(owner)
}
wg.Wait()
close(results)
successes, conflicts := 0, 0
for err := range results {
if err == nil {
successes++
} else if errors.Is(err, ErrConflict) {
conflicts++
} else {
t.Fatal(err)
}
}
if successes != 1 || conflicts != 1 {
t.Fatalf("successes=%d conflicts=%d", successes, conflicts)
}
if _, err := st.EnsureProject("alice", "rename-a", "Rename source"); err != nil {
t.Fatal(err)
}
if _, err := st.RenameOwnedProject("alice", "rename-a", "Taken"); !errors.Is(err, ErrConflict) {
t.Fatalf("rename collision: %v", err)
}
available, err := st.ProjectNameAvailable("Taken", "")
if err != nil || available {
t.Fatalf("availability=%v %v", available, err)
}
}
func TestProjectProjectionAdmissionTrimsLegacyWhitespace(t *testing.T) {
st := openTestStore(t)
if _, err := st.CreateProject("legacy", "\tReserved\u00a0"); err != nil {
t.Fatal(err)
}
if _, err := st.EnsureProject("other", "new-id", "Reserved"); !errors.Is(err, ErrConflict) {
t.Fatalf("trimmed legacy admission: %v", err)
}
}