package reconcile import ( "testing" "gitea.dooplex.hu/admin/felhom-agent/internal/authz" ) func TestClassify_BenignClasses(t *testing.T) { for _, c := range []OpClass{ClassStart, ClassStop, ClassSetConfig, ClassCreate, ClassRestart} { if got := Classify(c, Provenance{}); got != Benign { t.Errorf("Classify(%s) = %s, want benign", c, got) } } } func TestClassify_DestructiveClassesNeedSignature(t *testing.T) { for _, c := range []OpClass{ClassGuestDestroy, ClassStorageWipe, ClassRestoreOverwrite, ClassDecommission, ClassKeyRotation} { if got := Classify(c, Provenance{}); got != Destructive { t.Errorf("Classify(%s) = %s, want destructive", c, got) } } } func TestClassify_InternalProvenanceMakesDestroyBenign(t *testing.T) { // Same-transaction create → compensating rollback is benign (§10). if got := Classify(ClassGuestDestroy, Provenance{SameTxnCreated: true}); got != Benign { t.Errorf("same-txn destroy = %s, want benign", got) } // Agent-tagged scratch teardown is benign (§8). if got := Classify(ClassGuestDestroy, Provenance{AgentTaggedScratch: true}); got != Benign { t.Errorf("scratch destroy = %s, want benign", got) } } func TestClassify_KeyRotationAlwaysDestructive(t *testing.T) { // Even with internal provenance, key-rotation stays signed (role-scoping decides // which key) — provenance flags don't apply to it. if got := Classify(ClassKeyRotation, Provenance{SameTxnCreated: true, AgentTaggedScratch: true}); got != Destructive { t.Errorf("key_rotation = %s, want destructive", got) } } func TestClassify_UnknownClassFailsSafe(t *testing.T) { if got := Classify(OpClass("totally_unknown_op"), Provenance{}); got != Destructive { t.Errorf("unknown class = %s, want destructive (fail-safe)", got) } } func TestRoleAuthorizes(t *testing.T) { op := authz.RoleOperational rec := authz.RoleRecovery cases := []struct { role authz.KeyRole class OpClass want bool }{ {op, ClassGuestDestroy, true}, // operational does ordinary destructive {op, ClassDecommission, true}, // {op, ClassKeyRotation, true}, // operational does planned rotation {rec, ClassGuestDestroy, false}, // recovery may NOT do ordinary destructive {rec, ClassStorageWipe, false}, // {rec, ClassKeyRotation, true}, // recovery authorizes ONLY rotation } for _, c := range cases { if got := roleAuthorizes(c.role, c.class); got != c.want { t.Errorf("roleAuthorizes(%s, %s) = %v, want %v", c.role, c.class, got, c.want) } } }