Files
Assistant/tests/walking_skeleton/focus_isolation_test.go
2026-08-16 15:22:49 +03:00

85 lines
4.1 KiB
Go

package walking_skeleton_test
import (
"errors"
"testing"
"git.cin.su/m/Assistant/contracts/authority"
"git.cin.su/m/Assistant/contracts/identity"
situationcontract "git.cin.su/m/Assistant/contracts/situation"
workcontract "git.cin.su/m/Assistant/contracts/work"
situation "git.cin.su/m/Assistant/modules/situation/domain"
work "git.cin.su/m/Assistant/modules/work/domain"
"git.cin.su/m/Assistant/platform/ids"
)
func TestWS001FocusIsolationPreventsWrongProjectMutation(t *testing.T) {
actor := identity.ActorID(ids.ID("actor"))
scope := authority.ScopeID(ids.ID("scope"))
project := situationcontract.ProjectID(ids.ID("same-repository"))
goalA := workcontract.GoalID(ids.ID("goal-a"))
goalB := workcontract.GoalID(ids.ID("goal-b"))
bindings := []situationcontract.FocusBinding{
{ID: situationcontract.FocusBindingID(ids.ID("focus-a")), ActorID: actor, SessionID: situationcontract.SessionID(ids.ID("session-a")), Workspace: situationcontract.WorkspaceID(ids.ID("worktree-a")), ProjectID: project, GoalID: goalA, ScopeID: scope, Active: true},
{ID: situationcontract.FocusBindingID(ids.ID("focus-b")), ActorID: actor, SessionID: situationcontract.SessionID(ids.ID("session-b")), Workspace: situationcontract.WorkspaceID(ids.ID("worktree-b")), ProjectID: project, GoalID: goalB, ScopeID: scope, Active: true},
}
resolver := situation.NewResolver(bindings)
store := work.NewStore([]work.Task{
{ID: workcontract.TaskID(ids.ID("task-a")), GoalID: goalA, ScopeID: scope, Revision: 1},
{ID: workcontract.TaskID(ids.ID("task-b")), GoalID: goalB, ScopeID: scope, Revision: 1},
})
// The identical phrase is intentionally not supplied to resolution: runtime
// session/workspace bindings, including two worktrees of one repository,
// are the only project-selection inputs.
focusA, err := resolver.Resolve(situationcontract.RuntimeBinding{SessionID: bindings[0].SessionID, Workspace: bindings[0].Workspace})
if err != nil {
t.Fatal(err)
}
focusB, err := resolver.Resolve(situationcontract.RuntimeBinding{SessionID: bindings[1].SessionID, Workspace: bindings[1].Workspace})
if err != nil {
t.Fatal(err)
}
if focusA.GoalID == focusB.GoalID {
t.Fatal("two worktrees resolved to the same goal")
}
wrongTarget := workcontract.TaskTarget{TaskID: workcontract.TaskID(ids.ID("task-b")), GoalID: goalB, ScopeID: scope, Revision: 1}
if err := situation.ValidateTaskTarget(focusA, wrongTarget); !errors.Is(err, situation.ErrTargetMismatch) {
t.Fatalf("wrong cognition target error = %v, want ErrTargetMismatch", err)
}
targetA := workcontract.TaskTarget{TaskID: workcontract.TaskID(ids.ID("task-a")), GoalID: goalA, ScopeID: scope, Revision: 1}
if err := situation.ValidateTaskTarget(focusA, targetA); err != nil {
t.Fatal(err)
}
committed, err := store.Commit(targetA)
if err != nil {
t.Fatal(err)
}
if committed.Revision != 2 {
t.Fatalf("revision = %d, want 2", committed.Revision)
}
if _, err := store.Commit(targetA); !errors.Is(err, work.ErrRevisionConflict) {
t.Fatalf("stale revision error = %v, want ErrRevisionConflict", err)
}
if _, err := store.Commit(wrongTarget); err != nil {
t.Fatalf("other context must retain its original revision: %v", err)
}
}
func TestWS001RejectsStaleAndAmbiguousFocusWithoutMutation(t *testing.T) {
runtime := situationcontract.RuntimeBinding{SessionID: situationcontract.SessionID(ids.ID("session")), Workspace: situationcontract.WorkspaceID(ids.ID("workspace"))}
stale := situation.NewResolver([]situationcontract.FocusBinding{{SessionID: runtime.SessionID, Workspace: runtime.Workspace, Active: false}})
if _, err := stale.Resolve(runtime); !errors.Is(err, situation.ErrStaleFocus) {
t.Fatalf("stale error = %v, want ErrStaleFocus", err)
}
ambiguous := situation.NewResolver([]situationcontract.FocusBinding{
{ID: situationcontract.FocusBindingID(ids.ID("one")), SessionID: runtime.SessionID, Workspace: runtime.Workspace, Active: true},
{ID: situationcontract.FocusBindingID(ids.ID("two")), SessionID: runtime.SessionID, Workspace: runtime.Workspace, Active: true},
})
if _, err := ambiguous.Resolve(runtime); !errors.Is(err, situation.ErrAmbiguousFocus) {
t.Fatalf("ambiguity error = %v, want ErrAmbiguousFocus", err)
}
}