package txn import ( "context" "errors" "os" "testing" ) // TestApplyRefusesUnauthorized (P04, T4, C-44) verifies that Apply // returns ErrUnauthorized when the Authorize hook returns an error. // The apply path MUST refuse to run without a verified operator // token. func TestApplyRefusesUnauthorized(t *testing.T) { tr := &authMockTransport{} opts := ApplyOptions{ Namespace: "myapp", Authorize: func(ctx context.Context) (string, error) { return "", errors.New("ORCA_OIDC_TOKEN not set") }, } err := Apply(context.Background(), "T-deadbeefdeadbeef", "lead.example.com", tr, opts) if err == nil { t.Fatal("expected error when Authorize fails, got nil") } if !errors.Is(err, ErrUnauthorized) { t.Errorf("expected ErrUnauthorized, got %v", err) } } // TestApplyAuthorizesWithHook verifies that Apply proceeds when the // Authorize hook returns nil, and that the actor is logged. func TestApplyAuthorizesWithHook(t *testing.T) { tr := &authMockTransport{execOut: []byte("applied\n")} opts := ApplyOptions{ Namespace: "myapp", Authorize: func(ctx context.Context) (string, error) { return "oidc:operator@example.com", nil }, } err := Apply(context.Background(), "T-deadbeefdeadbeef", "lead.example.com", tr, opts) // We expect a non-ErrUnauthorized error here because the mock // transport's orca-pull.sh path doesn't exist; the point is that // the apply got PAST the authorize hook. if err != nil && errors.Is(err, ErrUnauthorized) { t.Errorf("apply should not be refused after successful authorize: %v", err) } } // TestApplyNoAuthorizeHookSkipsCheck verifies that when Authorize is // nil (legacy/test path), the apply proceeds without an auth check. // This preserves backward compat for tests that call Apply directly. func TestApplyNoAuthorizeHookSkipsCheck(t *testing.T) { tr := &authMockTransport{execOut: []byte("applied\n")} opts := ApplyOptions{Namespace: "myapp"} err := Apply(context.Background(), "T-deadbeefdeadbeef", "lead.example.com", tr, opts) // Any error is fine as long as it's not ErrUnauthorized. if err != nil && errors.Is(err, ErrUnauthorized) { t.Errorf("apply should skip auth when Authorize is nil: %v", err) } } // authMockTransport is a minimal Transport for the auth tests. type authMockTransport struct { execOut []byte execErr error } func (m *authMockTransport) WriteFileIdempotent(ctx context.Context, peer, path string, content []byte, mode os.FileMode) (bool, error) { return true, nil } func (m *authMockTransport) Exec(ctx context.Context, peer, cmd string) ([]byte, error) { return m.execOut, m.execErr }