diff --git a/app/apps/shell-super-app/tests/integration/auth-runtime.test.ts b/app/apps/shell-super-app/tests/integration/auth-runtime.test.ts index ffd9c4ec5..3a96a5ede 100644 --- a/app/apps/shell-super-app/tests/integration/auth-runtime.test.ts +++ b/app/apps/shell-super-app/tests/integration/auth-runtime.test.ts @@ -213,7 +213,7 @@ const installedPageCatalog = (): InstalledModuleCatalog => }, ]); -test('creates, resolves, persists, revokes, and signs out a Better Auth session', async () => { +void test('creates, resolves, persists, revokes, and signs out a Better Auth session', async () => { const configuration = await runEffectTestPromise(loadAuthConfig()); const corePool = new Pool({ connectionString: configuration.connectionString }); const coreDatabase = await runEffectTestPromise( @@ -370,7 +370,7 @@ test('creates, resolves, persists, revokes, and signs out a Better Auth session' const invalid = await runEffectTestPromise( Effect.flip(authentication.signIn(email, 'wrong-password', requestHeaders)), ); - assert.equal(invalid._tag, 'InvalidCredentialsError'); + assert.ok(Predicate.isTagged(invalid, 'InvalidCredentialsError')); const anonymousRuntime = makeShellAuthenticationApiRuntime( authenticationLayer, @@ -1013,7 +1013,7 @@ test('creates, resolves, persists, revokes, and signs out a Better Auth session' .pipe(Effect.provide(authenticationContextLayer)), ), ); - assert.equal(revoked._tag, 'OntosIdentityForbiddenError'); + assert.ok(Predicate.isTagged(revoked, 'OntosIdentityForbiddenError')); const forbiddenModulesResponse = await unavailableHandler.handler( new Request(`${configuration.baseUrl}/shell/composition`, { headers: authenticatedHeaders, @@ -1066,7 +1066,7 @@ test('creates, resolves, persists, revokes, and signs out a Better Auth session' } }); -test('selects, lists, switches, revalidates, and upgrades a multi-tenant session', async () => { +void test('selects, lists, switches, revalidates, and upgrades a multi-tenant session', async () => { const multiEmail = 'better-auth-multi-tenant@example.test'; const firstTenantId = '31000000-0000-4000-8000-000000000001'; const secondTenantId = '31000000-0000-4000-8000-000000000002'; @@ -1738,7 +1738,7 @@ test('selects, lists, switches, revalidates, and upgrades a multi-tenant session .pipe(Effect.provide(multiAuthenticationContextLayer)), ), ); - assert.equal(revokedSession._tag, 'OntosIdentityForbiddenError'); + assert.ok(Predicate.isTagged(revokedSession, 'OntosIdentityForbiddenError')); await runEffectTestPromise( coreDatabase .update(principalAuthBindings) @@ -1769,7 +1769,7 @@ test('selects, lists, switches, revalidates, and upgrades a multi-tenant session .pipe(Effect.provide(multiAuthenticationContextLayer)), ), ); - assert.equal(sessionWithRemovedBinding._tag, 'OntosIdentityForbiddenError'); + assert.ok(Predicate.isTagged(sessionWithRemovedBinding, 'OntosIdentityForbiddenError')); } finally { await Promise.all(handlers.map(async ({ dispose }) => await dispose())); await cleanup(); diff --git a/app/apps/shell-super-app/tests/integration/identity-modes-runtime.test.ts b/app/apps/shell-super-app/tests/integration/identity-modes-runtime.test.ts index 9f480a544..205224005 100644 --- a/app/apps/shell-super-app/tests/integration/identity-modes-runtime.test.ts +++ b/app/apps/shell-super-app/tests/integration/identity-modes-runtime.test.ts @@ -463,7 +463,7 @@ void test('verifies provider keys and completes live support impersonation with ); await runEffectTestPromise(keys.setEnabled(verified.providerKeyId, false)); const invalidKey = await runEffectTestPromise(Effect.flip(keys.verify(issued.secret))); - assert.equal(invalidKey._tag, 'ApiKeyCredentialInvalidError'); + assert.ok(Predicate.isTagged(invalidKey, 'ApiKeyCredentialInvalidError')); const managedPrincipal = await runEffectTestPromise( providePrincipalManagementRepository( @@ -580,7 +580,7 @@ void test('verifies provider keys and completes live support impersonation with const incompleteImpersonation = await runEffectTestPromise( Effect.flip(provideContextAccess(authentication.resolveTenantContext(impersonatedHeaders))), ); - assert.equal(incompleteImpersonation._tag, 'OntosIdentityForbiddenError'); + assert.ok(Predicate.isTagged(incompleteImpersonation, 'OntosIdentityForbiddenError')); await runEffectTestPromise( authDatabase .update(session) @@ -596,7 +596,7 @@ void test('verifies provider keys and completes live support impersonation with const mismatchedImpersonationReason = await runEffectTestPromise( Effect.flip(provideContextAccess(authentication.resolveTenantContext(impersonatedHeaders))), ); - assert.equal(mismatchedImpersonationReason._tag, 'OntosIdentityForbiddenError'); + assert.ok(Predicate.isTagged(mismatchedImpersonationReason, 'OntosIdentityForbiddenError')); await runEffectTestPromise( authDatabase .update(session) @@ -626,7 +626,7 @@ void test('verifies provider keys and completes live support impersonation with const revokedImpersonation = await runEffectTestPromise( Effect.flip(provideContextAccess(authentication.resolveTenantContext(impersonatedHeaders))), ); - assert.equal(revokedImpersonation._tag, 'OntosIdentityForbiddenError'); + assert.ok(Predicate.isTagged(revokedImpersonation, 'OntosIdentityForbiddenError')); const stopped = await runEffectTestPromise( provideContextAccess( providePrincipalManagementRepository( diff --git a/app/apps/shell-super-app/tests/unit/auth-config.test.ts b/app/apps/shell-super-app/tests/unit/auth-config.test.ts index 5f2090974..b7494d9ed 100644 --- a/app/apps/shell-super-app/tests/unit/auth-config.test.ts +++ b/app/apps/shell-super-app/tests/unit/auth-config.test.ts @@ -1,6 +1,6 @@ import { runEffectTestPromise } from '@app/core-runtime/testing/effect-runtime'; import { expect, test } from '@rstest/core'; -import { Effect } from 'effect'; +import { Effect, Predicate } from 'effect'; import { parseAuthConfig } from '../../api/auth/config.ts'; import { parseGatewayIssuerConfig } from '../../api/auth/gateway-issuer-config.ts'; @@ -39,8 +39,8 @@ test('requires a strong secret and PostgreSQL URL in the typed error channel', a ), ), ]).then(([secretError, databaseError]) => { - expect(secretError._tag).toBe('AuthConfigError'); - expect(databaseError._tag).toBe('AuthConfigError'); + expect(Predicate.isTagged(secretError, 'AuthConfigError')).toBe(true); + expect(Predicate.isTagged(databaseError, 'AuthConfigError')).toBe(true); })); test('keeps gateway signing configuration independent from Better Auth configuration', async () => { @@ -48,5 +48,5 @@ test('keeps gateway signing configuration independent from Better Auth configura const gatewayError = await runEffectTestPromise(Effect.flip(parseGatewayIssuerConfig({}))); expect(authentication.baseUrl).toBe('http://localhost:3020'); - expect(gatewayError._tag).toBe('GatewayIssuerConfigError'); + expect(Predicate.isTagged(gatewayError, 'GatewayIssuerConfigError')).toBe(true); }); diff --git a/app/apps/shell-super-app/tests/unit/auth-contract.test.ts b/app/apps/shell-super-app/tests/unit/auth-contract.test.ts index fc9e40e55..aaf48fc2e 100644 --- a/app/apps/shell-super-app/tests/unit/auth-contract.test.ts +++ b/app/apps/shell-super-app/tests/unit/auth-contract.test.ts @@ -1,6 +1,6 @@ import { runEffectTestPromise } from '@app/core-runtime/testing/effect-runtime'; import { expect, test } from '@rstest/core'; -import { DateTime, Effect, Schema } from 'effect'; +import { DateTime, Effect, Schema, Predicate } from 'effect'; import { CurrentSessionSchema, AvailableLegalEntitiesResponseSchema, @@ -233,7 +233,7 @@ test('validates tenant UUIDs and strips all non-contract fields', async () => { const invalidPayload = await runEffectTestPromise( Effect.flip(Schema.decodeUnknownEffect(SwitchTenantPayloadSchema)({ tenantId: 'not-a-uuid' })), ); - expect(invalidPayload._tag).toBe('SchemaError'); + expect(Predicate.isTagged(invalidPayload, 'SchemaError')).toBe(true); }); test('rejects malformed credentials through Effect Schema', async () => { @@ -245,7 +245,7 @@ test('rejects malformed credentials through Effect Schema', async () => { }), ), ); - expect(error._tag).toBe('SchemaError'); + expect(Predicate.isTagged(error, 'SchemaError')).toBe(true); }); test('requires lifecycle reasons and strips provider-private API key identifiers', async () => { @@ -270,8 +270,8 @@ test('requires lifecycle reasons and strips provider-private API key identifiers }), ), ); - expect(missingPrincipalReason._tag).toBe('SchemaError'); - expect(missingRevocationReason._tag).toBe('SchemaError'); + expect(Predicate.isTagged(missingPrincipalReason, 'SchemaError')).toBe(true); + expect(Predicate.isTagged(missingRevocationReason, 'SchemaError')).toBe(true); const lifecycle = await runEffectTestPromise( Schema.decodeUnknownEffect(ApiKeyLifecycleResponseSchema)({ diff --git a/app/apps/shell-super-app/tests/unit/identity-lifecycle.test.ts b/app/apps/shell-super-app/tests/unit/identity-lifecycle.test.ts index 4e6587352..909ec358e 100644 --- a/app/apps/shell-super-app/tests/unit/identity-lifecycle.test.ts +++ b/app/apps/shell-super-app/tests/unit/identity-lifecycle.test.ts @@ -6,7 +6,7 @@ import { IdentityTargetInvalidError, PrincipalBindingMissingError, } from '@app/core-runtime'; -import { Effect, Redacted } from 'effect'; +import { Effect, Redacted, Predicate } from 'effect'; import { ApiKeyProviderUnavailableError, ApiKeyStateInconsistentError, @@ -89,7 +89,7 @@ test('compensates a failed Core bind and never exposes the provider key identifi ), ); expect(failure).toBe(bindFailure); - expect(failure._tag).toBe('IdentityTargetInvalidError'); + expect(Predicate.isTagged(failure, 'IdentityTargetInvalidError')).toBe(true); expect(disabled).toEqual(['private-provider-key-id']); }); @@ -117,7 +117,7 @@ test('preserves resolver lifecycle failures instead of rewriting them as an outa ); expect(failure).toBe(resolverFailure); - expect(failure._tag).toBe('PrincipalBindingMissingError'); + expect(Predicate.isTagged(failure, 'PrincipalBindingMissingError')).toBe(true); }); test('preserves a typed Core status-transition failure before touching provider state', async () => { @@ -151,7 +151,7 @@ test('preserves a typed Core status-transition failure before touching provider ); expect(failure).toBe(actionFailure); - expect(failure._tag).toBe('IdentityTargetInvalidError'); + expect(Predicate.isTagged(failure, 'IdentityTargetInvalidError')).toBe(true); expect(providerCalls).toBe(0); }); @@ -456,7 +456,7 @@ test('cleans one bounded pending batch and requires a retry before issuing anoth ), ); - expect(failure._tag).toBe('IdentityLifecycleOperationError'); + expect(Predicate.isTagged(failure, 'IdentityLifecycleOperationError')).toBe(true); expect(disabled).toEqual(['bounded-orphan']); expect(issueCalls).toBe(0); }); diff --git a/app/apps/shell-super-app/tests/unit/layout.test.tsx b/app/apps/shell-super-app/tests/unit/layout.test.tsx index 79cb6ef44..114610fa8 100644 --- a/app/apps/shell-super-app/tests/unit/layout.test.tsx +++ b/app/apps/shell-super-app/tests/unit/layout.test.tsx @@ -305,10 +305,8 @@ test('renders the account Menu last and dispatches only the logout command by ke const header = document.querySelector('header[aria-label="Dashboard header"]'); const trigger = screen.getByRole('button', { name: 'Ada Lovelace' }); expect(header?.lastElementChild?.contains(trigger)).toBe(true); - const accountMenu = header?.lastElementChild; - expect(accountMenu instanceof HTMLElement ? accountMenu.dataset['position'] : undefined).toBe( - 'end', - ); + const accountMenu = header?.querySelector(':scope > :last-child'); + expect(accountMenu?.dataset['position']).toBe('end'); trigger.focus(); await user.keyboard('{Enter}'); diff --git a/app/apps/shell-super-app/tests/unit/legal-entity-selection.test.ts b/app/apps/shell-super-app/tests/unit/legal-entity-selection.test.ts index 94fe34ba9..c630d75e4 100644 --- a/app/apps/shell-super-app/tests/unit/legal-entity-selection.test.ts +++ b/app/apps/shell-super-app/tests/unit/legal-entity-selection.test.ts @@ -3,7 +3,7 @@ import assert from 'node:assert/strict'; import { test } from '@rstest/core'; import { ContextAccess, LegalEntityContext } from '@app/core-runtime'; import type { ContextAccessService, LegalEntityContextService } from '@app/core-runtime'; -import { Effect, Layer } from 'effect'; +import { Effect, Layer, Predicate } from 'effect'; import { resolveAuthorizedLegalEntities, validateAuthorizedLegalEntity, @@ -110,7 +110,7 @@ test('fails closed for authorization uncertainty and validates a switch independ ), ), ); - assert.equal(unavailable._tag, 'LegalEntitySelectionUnavailableError'); + assert.ok(Predicate.isTagged(unavailable, 'LegalEntitySelectionUnavailableError')); assert.deepEqual( await runEffectTestPromise( provideSelectionServices( diff --git a/app/packages/core-runtime/src/actions/repository.ts b/app/packages/core-runtime/src/actions/repository.ts index e7c6f737b..034215bde 100644 --- a/app/packages/core-runtime/src/actions/repository.ts +++ b/app/packages/core-runtime/src/actions/repository.ts @@ -106,7 +106,7 @@ const normalizeForHash = (value: Value, seen: WeakSet): Canonical if (Predicate.isBigInt(value)) { return ['bigint', value.toString(10)]; } - if (value instanceof Date) { + if (Predicate.isDate(value)) { return ['date', value.toISOString()]; } if (Array.isArray(value)) { diff --git a/app/packages/core-runtime/src/outbox/runtime.ts b/app/packages/core-runtime/src/outbox/runtime.ts index a7fe9bc42..3917d2317 100644 --- a/app/packages/core-runtime/src/outbox/runtime.ts +++ b/app/packages/core-runtime/src/outbox/runtime.ts @@ -17,10 +17,9 @@ import { import { OutboxHandlerExecutionError, OutboxPayloadDecodeError, - OutboxPersistenceError, OutboxWorkerDescriptorError, } from './errors.ts'; -import type { OutboxClaimLostError } from './errors.ts'; +import type { OutboxClaimLostError, OutboxPersistenceError } from './errors.ts'; import { OutboxRepository } from './repository.ts'; import type { OutboxClaim, OutboxRepositoryService as OutboxRepositoryPort } from './repository.ts'; @@ -101,7 +100,7 @@ const validateCycleInput = Effect.fn('OutboxRuntime.validateCycleInput')( } const registrations = yield* Effect.try({ catch: (error) => - error instanceof OutboxWorkerDescriptorError + Schema.is(OutboxWorkerDescriptorError)(error) ? error : descriptorFailure('The Outbox Worker descriptor set is invalid'), try: () => validateOutboxWorkerRegistrations(input.registrations), @@ -302,9 +301,7 @@ const processNextOutboxDelivery = Effect.fn('makeOutboxRuntime.processNextDelive reason: 'The Outbox Message payload does not match its published schema', }); const status = yield* repository.fail(claim, decodeError.reason, execution.now).pipe( - Effect.tapError((error) => - error instanceof OutboxPersistenceError ? logUnexpectedPersistence(claim) : Effect.void, - ), + Effect.tapErrorTag('OutboxPersistenceError', () => logUnexpectedPersistence(claim)), (effect) => withOutcomeSpan(effect, claim, 'payload_decode_failure'), ); return { @@ -342,9 +339,7 @@ const processNextOutboxDelivery = Effect.fn('makeOutboxRuntime.processNextDelive : 'The Outbox Worker handler returned a declared failure', }); const status = yield* repository.fail(claim, executionError.reason, execution.now).pipe( - Effect.tapError((error) => - error instanceof OutboxPersistenceError ? logUnexpectedPersistence(claim) : Effect.void, - ), + Effect.tapErrorTag('OutboxPersistenceError', () => logUnexpectedPersistence(claim)), (effect) => withOutcomeSpan(effect, claim, 'handler_failure'), ); return { @@ -356,9 +351,7 @@ const processNextOutboxDelivery = Effect.fn('makeOutboxRuntime.processNextDelive } yield* repository.complete(claim, execution.now).pipe( - Effect.tapError((error) => - error instanceof OutboxPersistenceError ? logUnexpectedPersistence(claim) : Effect.void, - ), + Effect.tapErrorTag('OutboxPersistenceError', () => logUnexpectedPersistence(claim)), (effect) => withOutcomeSpan(effect, claim, 'success'), ); return { ...claimedState, succeeded: claimedState.succeeded + 1 }; diff --git a/app/packages/core-runtime/src/testing/live-operations.ts b/app/packages/core-runtime/src/testing/live-operations.ts index e30ebe475..839b8bb8f 100644 --- a/app/packages/core-runtime/src/testing/live-operations.ts +++ b/app/packages/core-runtime/src/testing/live-operations.ts @@ -66,7 +66,6 @@ export type LiveOperationFixtureConfiguration = class LiveOperationFixtureError extends Schema.TaggedError()( 'LiveOperationFixtureError', { - commitIndeterminate: Schema.optional(Schema.Literal(true)), reason: Schema.String, }, ) {} diff --git a/app/packages/core-runtime/tests/integration/action-permission.test.ts b/app/packages/core-runtime/tests/integration/action-permission.test.ts index e0e80af38..a1a66d813 100644 --- a/app/packages/core-runtime/tests/integration/action-permission.test.ts +++ b/app/packages/core-runtime/tests/integration/action-permission.test.ts @@ -1,7 +1,7 @@ import { runEffectTestPromise } from '@app/core-runtime/testing/effect-runtime'; import { v1 } from '@authzed/authzed-node'; import { and, eq } from 'drizzle-orm'; -import { Effect, Exit, Schema, flow } from 'effect'; +import { Effect, Exit, Schema, flow, Predicate } from 'effect'; import assert from 'node:assert/strict'; import { randomUUID } from 'node:crypto'; import test, { after, before } from 'node:test'; @@ -578,7 +578,7 @@ effectTest( .where(eq(outboxMessages.tenantId, tenantId)), ]); - assert.equal(failure._tag, 'ActionPermissionDenied'); + assert.ok(Predicate.isTagged(failure, 'ActionPermissionDenied')); assert.equal(failure.reason, 'The principal is not permitted to execute this Action'); assert.equal(executions.value, 0); assert.equal(invocation.status, 'rejected'); @@ -637,7 +637,7 @@ effectTest( ), ); - assert.equal(failure._tag, 'ActionPermissionDenied', kind); + assert.ok(Predicate.isTagged(failure, 'ActionPermissionDenied'), kind); assert.equal(executions.value, 0, kind); }), { concurrency: 1, discard: true }, @@ -678,10 +678,10 @@ effectTest( .from(auditEvents) .where(eq(auditEvents.actionInvocationId, invocation.actionInvocationId)); - assert.deepEqual( - results.map((result) => result._tag), - ['ActionPermissionDenied', 'ActionPermissionDenied'], - ); + assert.equal(results.length, 2); + for (const result of results) { + assert.ok(Predicate.isTagged(result, 'ActionPermissionDenied')); + } assert.equal(executions.value, 0); assert.equal(invocation.status, 'rejected'); assert.equal(audits.length, 1); @@ -773,7 +773,7 @@ effectTest( .from(auditEvents) .where(eq(auditEvents.actionInvocationId, invocation.actionInvocationId)); - assert.equal(failure._tag, 'ActionTransactionError', stage); + assert.ok(Predicate.isTagged(failure, 'ActionTransactionError'), stage); assert.equal(executions.value, 0, stage); assert.equal(invocation.status, 'received', stage); assert.equal(invocation.completedAt, null, stage); @@ -823,7 +823,7 @@ effectTest( ), ); - assert.equal(failure._tag, 'ActionPermissionCheckError'); + assert.ok(Predicate.isTagged(failure, 'ActionPermissionCheckError')); assert.equal(failure.reason.includes('invalid-integration-key'), false); assert.equal(executions.value, 0); assert.equal(invocation.status, 'received'); diff --git a/app/packages/core-runtime/tests/integration/action-runtime.test.ts b/app/packages/core-runtime/tests/integration/action-runtime.test.ts index 88e29e822..4be2144a7 100644 --- a/app/packages/core-runtime/tests/integration/action-runtime.test.ts +++ b/app/packages/core-runtime/tests/integration/action-runtime.test.ts @@ -3,7 +3,7 @@ import { ConnectionError, SqlError, UnknownError } from 'effect/unstable/sql/Sql import assert from 'node:assert/strict'; // @effect-diagnostics asyncFunction:off globalDateInEffect:off -- Existing compatibility boundary; expires: 2026-12-31. import { and, eq } from 'drizzle-orm'; -import { Cause, Deferred, Effect, Exit, Fiber, Option, Schema } from 'effect'; +import { Cause, Deferred, Effect, Exit, Fiber, Option, Schema, Predicate } from 'effect'; import { randomUUID } from 'node:crypto'; import test, { after, before } from 'node:test'; import type { ActionHandlerContext } from '../../src/actions/context.ts'; @@ -71,8 +71,6 @@ const TestDomainRejected = Schema.TaggedError()('TestDom }); const TestStateIdSchema = Schema.String.pipe(Schema.brand('TestStateId')); -const FailureTagSchema = Schema.Struct({ _tag: Schema.String }); -const decodeFailureTag = Schema.decodeUnknownOption(FailureTagSchema); const ActionPolicyDeniedFailureSchema = Schema.TaggedStruct('ActionPolicyDenied', { policyReasonCode: Schema.String, reason: Schema.String, @@ -456,17 +454,8 @@ const makeRegistration = ({ (transaction) => Effect.succeed({ transaction }), ); -const failureTag = (exit: Exit.Exit): string | undefined => { - if (Exit.isSuccess(exit)) { - return undefined; - } - return Option.getOrUndefined( - Option.map( - Option.flatMap(Cause.findErrorOption(exit.cause), decodeFailureTag), - (failure) => failure._tag, - ), - ); -}; +const hasFailure = (exit: Exit.Exit, tag: string): boolean => + Exit.isFailure(exit) && Option.exists(Cause.findErrorOption(exit.cause), Predicate.isTagged(tag)); void test('rechecks business module state under the tenant lock and retries after Core recovery', async () => { await databasePromise(async (database) => { @@ -559,7 +548,7 @@ void test('rechecks business module state under the tenant lock and retries afte ); await runEffectTestPromise(Deferred.succeed(continuePolicy, null)); const denied = await firstAttempt; - assert.equal(failureTag(denied), 'ModuleStateDeniedError'); + assert.ok(hasFailure(denied, 'ModuleStateDeniedError')); assert.equal(handlerExecutions, 0); const [openInvocation] = await runEffectTestPromise( @@ -854,7 +843,7 @@ void test('atomically rejects denied global and same-owner MicroVertical Policie const failure = Exit.isFailure(exit) ? Option.flatMap(Cause.findErrorOption(exit.cause), decodeActionPolicyDeniedFailure) : Option.none(); - assert.equal(failureTag(exit), 'ActionPolicyDenied'); + assert.ok(hasFailure(exit, 'ActionPolicyDenied')); if (Option.isSome(failure)) { assert.equal(failure.value.reason, scenario.reason); assert.equal(failure.value.policyReasonCode, scenario.reasonCode); @@ -968,9 +957,8 @@ void test('rolls back every denied-Policy finalization persistence failure', asy .where(eq(auditEvents.actionInvocationId, invocation.actionInvocationId)), ); - assert.equal( - failureTag(exit), - 'ActionInvocationPersistenceError', + assert.ok( + hasFailure(exit, 'ActionInvocationPersistenceError'), Exit.isFailure(exit) ? Cause.pretty(exit.cause) : 'success', ); assert.equal(handlerExecutions, 0); @@ -1077,7 +1065,7 @@ void test('rolls back domain rejection, evidence persistence failure, and orphan .where(eq(domainEvents.actionInvocationId, invocationId)), ); - assert.equal(failureTag(exit), scenario.expectedTag); + assert.ok(hasFailure(exit, scenario.expectedTag)); assert.equal(states.length, 0); assert.equal(invocations[0]?.status, 'running'); assert.equal(invocations[0]?.completedAt, null); @@ -1174,7 +1162,7 @@ void test('rolls back every individual success-evidence persistence failure', as ]), ); - assert.equal(failureTag(exit), 'ActionTransactionError', stage); + assert.ok(hasFailure(exit, 'ActionTransactionError'), stage); assert.equal(states.length, 0, stage); assert.equal(invocation?.status, 'running', stage); assert.equal(invocation?.completedAt, null, stage); @@ -1237,8 +1225,8 @@ void test('keeps Policy rejection terminal and deduplicates repeated and concurr .where(eq(auditEvents.actionInvocationId, invocation.actionInvocationId)), ); - assert.equal(failureTag(first), 'ActionPolicyDenied'); - assert.equal(failureTag(retry), 'ActionInvocationStateError'); + assert.ok(hasFailure(first, 'ActionPolicyDenied')); + assert.ok(hasFailure(retry, 'ActionInvocationStateError')); assert.equal(evaluations, 1); assert.equal(handlerExecutions, 0); assert.equal(invocation.status, 'rejected'); @@ -1290,7 +1278,10 @@ void test('keeps Policy rejection terminal and deduplicates repeated and concurr .where(eq(auditEvents.actionInvocationId, concurrentInvocation.actionInvocationId)), ); - assert.deepEqual(concurrent.map(failureTag), ['ActionPolicyDenied', 'ActionPolicyDenied']); + assert.equal(concurrent.length, 2); + for (const outcome of concurrent) { + assert.ok(hasFailure(outcome, 'ActionPolicyDenied')); + } assert.equal(concurrentEvaluations, 2); assert.equal(handlerExecutions, 0); assert.equal(concurrentInvocation.status, 'rejected'); @@ -1369,7 +1360,7 @@ void test('never lets a losing Policy denial replace a running or successful inv ); assert.equal(successResult.value, 'same'); - assert.equal(failureTag(rejectedExit), 'ActionInvocationPersistenceError'); + assert.ok(hasFailure(rejectedExit, 'ActionInvocationPersistenceError')); assert.equal(invocation.status, 'succeeded'); assert.equal(audits.filter((row) => row.eventType === 'action.rejected').length, 0); }); @@ -1433,9 +1424,11 @@ void test('serializes concurrent requests and enforces committed, open-retry, an assert.equal(executions, 1); assert.equal(concurrentResults.filter(Exit.isSuccess).length, 1); - assert.deepEqual(concurrentResults.filter(Exit.isFailure).map(failureTag), [ - 'ActionAlreadyCommitted', - ]); + const failedResults = concurrentResults.filter(Exit.isFailure); + assert.equal(failedResults.length, 1); + for (const outcome of failedResults) { + assert.ok(hasFailure(outcome, 'ActionAlreadyCommitted')); + } const committedRetry = yield* Effect.exit( runtime.runAction({ @@ -1447,7 +1440,7 @@ void test('serializes concurrent requests and enforces committed, open-retry, an }, }), ); - assert.equal(failureTag(committedRetry), 'ActionAlreadyCommitted'); + assert.ok(hasFailure(committedRetry, 'ActionAlreadyCommitted')); assert.equal(executions, 1); const conflict = yield* Effect.exit( @@ -1456,7 +1449,7 @@ void test('serializes concurrent requests and enforces committed, open-retry, an payload: { value: 'different' }, }), ); - assert.equal(failureTag(conflict), 'ActionRequestHashConflict'); + assert.ok(hasFailure(conflict, 'ActionRequestHashConflict')); const openKey = 'open-retry'; const openModule = `test.open-retry.${tenantId}`; @@ -1472,7 +1465,7 @@ void test('serializes concurrent requests and enforces committed, open-retry, an transport: transport(openKey, openModule), }), ); - assert.equal(failureTag(rejected), 'TestDomainRejected'); + assert.ok(hasFailure(rejected, 'TestDomainRejected')); const retried = yield* runtime.runAction({ payload: { value: 'retryable' }, @@ -1619,7 +1612,7 @@ void test('resolves a lost commit acknowledgement from the durable succeeded mar }), ), ); - assert.equal(failureTag(first), 'ActionCommitIndeterminate'); + assert.ok(hasFailure(first, 'ActionCommitIndeterminate')); const resolvingRuntime = makeActionRuntime( database, @@ -1696,10 +1689,10 @@ void test('resolves a lost commit acknowledgement from the durable succeeded mar .where(eq(tenantModuleStates.moduleKey, moduleStateKey)), ); - assert.equal(failureTag(committedResolution), 'ActionAlreadyCommitted'); - assert.equal(failureTag(unauthorizedResolution), 'ActionInvocationNotFound'); - assert.equal(failureTag(unavailableResolution), 'ActionCommitIndeterminate'); - assert.equal(failureTag(resolved), 'ActionAlreadyCommitted'); + assert.ok(hasFailure(committedResolution, 'ActionAlreadyCommitted')); + assert.ok(hasFailure(unauthorizedResolution, 'ActionInvocationNotFound')); + assert.ok(hasFailure(unavailableResolution, 'ActionCommitIndeterminate')); + assert.ok(hasFailure(resolved, 'ActionAlreadyCommitted')); assert.equal(invocations[0]?.status, 'succeeded'); assert.equal(states.length, 1); @@ -1740,7 +1733,7 @@ void test('resolves a lost commit acknowledgement from the durable succeeded mar }), ), ); - assert.equal(failureTag(openFirst), 'ActionCommitIndeterminate'); + assert.ok(hasFailure(openFirst, 'ActionCommitIndeterminate')); const openInvocations = await runEffectTestPromise( database.executor @@ -1771,7 +1764,7 @@ void test('resolves a lost commit acknowledgement from the durable succeeded mar .where(eq(tenantModuleStates.moduleKey, openModuleStateKey)), ); - assert.equal(openResolution._tag, 'ActionCommitOpen'); + assert.ok(Predicate.isTagged(openResolution, 'ActionCommitOpen')); assert.equal(openResolved.value, 'rolled-back-with-lost-ack'); assert.equal(openStates.length, 1); }); @@ -1869,7 +1862,7 @@ void test('persists no invocation or evidence for every non-writable business mo }), ), ); - assert.equal(failureTag(exit), 'ModuleStateDeniedError', state); + assert.ok(hasFailure(exit, 'ModuleStateDeniedError'), state); const invocations = await runEffectTestPromise( database.executor .select() @@ -1901,7 +1894,7 @@ void test('persists no invocation or evidence for every non-writable business mo }), ), ); - assert.equal(failureTag(missingExit), 'ModuleStateDeniedError'); + assert.ok(hasFailure(missingExit, 'ModuleStateDeniedError')); assert.equal(handlerExecutions, 1); }); }); diff --git a/app/packages/core-runtime/tests/integration/identity-runtime.test.ts b/app/packages/core-runtime/tests/integration/identity-runtime.test.ts index e00e59de8..adeeafca4 100644 --- a/app/packages/core-runtime/tests/integration/identity-runtime.test.ts +++ b/app/packages/core-runtime/tests/integration/identity-runtime.test.ts @@ -467,7 +467,7 @@ effectTest( }), ), ); - assert.equal(systemDenied._tag, 'ActionPermissionDenied'); + assert.ok(Predicate.isTagged(systemDenied, 'ActionPermissionDenied')); const systemTenantMember = relationship( 'tenant', tenantId, diff --git a/app/packages/core-runtime/tests/integration/legal-entity-context.test.ts b/app/packages/core-runtime/tests/integration/legal-entity-context.test.ts index c8fb46bad..fe65078e5 100644 --- a/app/packages/core-runtime/tests/integration/legal-entity-context.test.ts +++ b/app/packages/core-runtime/tests/integration/legal-entity-context.test.ts @@ -4,7 +4,7 @@ import { } from '@app/core-runtime/testing/effect-runtime'; import { eq } from 'drizzle-orm'; -import { Effect, Exit as NativeExit, Scope as NativeScope } from 'effect'; +import { Effect, Exit as NativeExit, Scope as NativeScope, Predicate } from 'effect'; import assert from 'node:assert/strict'; import test, { after as afterNativeDatabase } from 'node:test'; import { Pool } from 'pg'; @@ -110,9 +110,9 @@ effectTest( legalName: 'Zeta entity', }); const inactiveError = yield* Effect.flip(context.validateSelection(tenantOne, suspended)); - assert.equal(inactiveError._tag, 'LegalEntityContextInactiveError'); + assert.ok(Predicate.isTagged(inactiveError, 'LegalEntityContextInactiveError')); const missingError = yield* Effect.flip(context.validateSelection(tenantOne, foreign)); - assert.equal(missingError._tag, 'LegalEntityContextMissingError'); + assert.ok(Predicate.isTagged(missingError, 'LegalEntityContextMissingError')); }).pipe( Effect.ensuring(cleanup.pipe(Effect.orDie)), Effect.ensuring(databaseEffect(pool.end.bind(pool)).pipe(Effect.orDie)), diff --git a/app/packages/core-runtime/tests/integration/module-state-gate.test.ts b/app/packages/core-runtime/tests/integration/module-state-gate.test.ts index 9f9cce005..c96678f80 100644 --- a/app/packages/core-runtime/tests/integration/module-state-gate.test.ts +++ b/app/packages/core-runtime/tests/integration/module-state-gate.test.ts @@ -1,7 +1,7 @@ import { runEffectTestPromise } from '@app/core-runtime/testing/effect-runtime'; // @effect-diagnostics asyncFunction:off -- Existing compatibility boundary; expires: 2026-12-31. import { and, eq } from 'drizzle-orm'; -import { Effect, Exit } from 'effect'; +import { Effect, Exit, Predicate } from 'effect'; import assert from 'node:assert/strict'; import { randomUUID } from 'node:crypto'; import test from 'node:test'; @@ -163,7 +163,7 @@ void test('batches tenant-isolated states once, rejects malformed/unavailable re const quarantined = await runEffectTestPromise( Effect.flip(gate.check(tenantTwoSnapshot, read)), ); - assert.equal(quarantined._tag, 'ModuleStateDeniedError'); + assert.ok(Predicate.isTagged(quarantined, 'ModuleStateDeniedError')); const missingDescriptor = defineTenantModuleEntrypoint({ access: 'read', @@ -178,7 +178,7 @@ void test('batches tenant-isolated states once, rejects malformed/unavailable re const missing = await runEffectTestPromise( Effect.flip(gate.check(missingSnapshot, missingDescriptor)), ); - assert.equal(missing._tag, 'ModuleStateDeniedError'); + assert.ok(Predicate.isTagged(missing, 'ModuleStateDeniedError')); await runEffectTestPromise( database.executor.transaction((transaction) => @@ -201,7 +201,7 @@ void test('batches tenant-isolated states once, rejects malformed/unavailable re Effect.flip(gate.recheckWrite(transaction, tenantOne, write)), ), ); - assert.equal(lockedDenial._tag, 'ModuleStateDeniedError'); + assert.ok(Predicate.isTagged(lockedDenial, 'ModuleStateDeniedError')); const unavailable = await runEffectTestPromise( Effect.flip( @@ -211,7 +211,7 @@ void test('batches tenant-isolated states once, rejects malformed/unavailable re ), ), ); - assert.equal(unavailable._tag, 'ModuleStateCheckUnavailableError'); + assert.ok(Predicate.isTagged(unavailable, 'ModuleStateCheckUnavailableError')); assert.doesNotMatch(unavailable.reason, /secret|db failure/u); const malformed = await runEffectTestPromise( @@ -222,7 +222,7 @@ void test('batches tenant-isolated states once, rejects malformed/unavailable re ), ), ); - assert.equal(malformed._tag, 'ModuleStateCheckUnavailableError'); + assert.ok(Predicate.isTagged(malformed, 'ModuleStateCheckUnavailableError')); assert.doesNotMatch(malformed.reason, /corrupt|storage/u); } finally { await runEffectTestPromise( diff --git a/app/packages/core-runtime/tests/integration/principal-management.test.ts b/app/packages/core-runtime/tests/integration/principal-management.test.ts index e945ded5d..1da0e2a30 100644 --- a/app/packages/core-runtime/tests/integration/principal-management.test.ts +++ b/app/packages/core-runtime/tests/integration/principal-management.test.ts @@ -5,7 +5,7 @@ import { // @effect-diagnostics asyncFunction:off -- Existing compatibility boundary; expires: 2026-12-31. import { eq } from 'drizzle-orm'; -import { Effect, Exit as NativeExit, Scope as NativeScope } from 'effect'; +import { Effect, Exit as NativeExit, Scope as NativeScope, Predicate } from 'effect'; import assert from 'node:assert/strict'; import { randomUUID } from 'node:crypto'; import test, { after as afterNativeDatabase } from 'node:test'; @@ -118,7 +118,7 @@ void test('persists managed key lifecycle without credential material and enforc ), ), ); - assert.equal(duplicate._tag, 'IdentityLifecycleConflictError'); + assert.ok(Predicate.isTagged(duplicate, 'IdentityLifecycleConflictError')); const missingReason = await runEffectTestPromise( database.transaction((transaction) => @@ -139,7 +139,7 @@ void test('persists managed key lifecycle without credential material and enforc ), ), ); - assert.equal(missingReason._tag, 'IdentityTargetInvalidError'); + assert.ok(Predicate.isTagged(missingReason, 'IdentityTargetInvalidError')); await runEffectTestPromise( database.transaction((transaction) => diff --git a/app/packages/core-runtime/tests/integration/principal-resolver.test.ts b/app/packages/core-runtime/tests/integration/principal-resolver.test.ts index 8d5adeab2..61a728070 100644 --- a/app/packages/core-runtime/tests/integration/principal-resolver.test.ts +++ b/app/packages/core-runtime/tests/integration/principal-resolver.test.ts @@ -4,7 +4,7 @@ import { } from '@app/core-runtime/testing/effect-runtime'; import { and, eq } from 'drizzle-orm'; -import { DateTime, Effect, Exit as NativeExit, Scope as NativeScope } from 'effect'; +import { DateTime, Effect, Exit as NativeExit, Scope as NativeScope, Predicate } from 'effect'; import assert from 'node:assert/strict'; import test, { after as afterNativeDatabase } from 'node:test'; import { Pool } from 'pg'; @@ -118,7 +118,7 @@ effectTest( const foreignResolution = yield* Effect.flip( resolver.resolveBetterAuthUserForTenant('foreign-better-auth-subject', tenantOne), ); - assert.equal(foreignResolution._tag, 'PrincipalBindingMissingError'); + assert.ok(Predicate.isTagged(foreignResolution, 'PrincipalBindingMissingError')); yield* database .update(principalAuthBindings) @@ -133,7 +133,7 @@ effectTest( const revokedResolution = yield* Effect.flip( resolver.resolveBetterAuthUserForTenant(subject, tenantOne), ); - assert.equal(revokedResolution._tag, 'PrincipalBindingInactiveError'); + assert.ok(Predicate.isTagged(revokedResolution, 'PrincipalBindingInactiveError')); yield* database .update(principalAuthBindings) @@ -146,7 +146,7 @@ effectTest( const inactivePrincipal = yield* Effect.flip( resolver.resolveBetterAuthUserForTenant(subject, tenantOne), ); - assert.equal(inactivePrincipal._tag, 'PrincipalInactiveError'); + assert.ok(Predicate.isTagged(inactivePrincipal, 'PrincipalInactiveError')); yield* database .update(principals) @@ -159,7 +159,7 @@ effectTest( const inactiveTenant = yield* Effect.flip( resolver.resolveBetterAuthUserForTenant(subject, tenantOne), ); - assert.equal(inactiveTenant._tag, 'TenantInactiveError'); + assert.ok(Predicate.isTagged(inactiveTenant, 'TenantInactiveError')); }).pipe( Effect.ensuring(cleanup.pipe(Effect.orDie)), Effect.ensuring(databaseEffect(pool.end.bind(pool)).pipe(Effect.orDie)), diff --git a/app/packages/core-runtime/tests/integration/search-persistence.test.ts b/app/packages/core-runtime/tests/integration/search-persistence.test.ts index 4cd1c0c22..9052c6bf8 100644 --- a/app/packages/core-runtime/tests/integration/search-persistence.test.ts +++ b/app/packages/core-runtime/tests/integration/search-persistence.test.ts @@ -3,7 +3,14 @@ import { makeEffectTestCallback, } from '@app/core-runtime/testing/effect-runtime'; -import { Effect, Function as Fn, Exit as NativeExit, Scope as NativeScope, Schema } from 'effect'; +import { + Effect, + Function as Fn, + Exit as NativeExit, + Scope as NativeScope, + Schema, + Predicate, +} from 'effect'; import assert from 'node:assert/strict'; import { randomUUID } from 'node:crypto'; import test, { after as afterNativeDatabase } from 'node:test'; @@ -245,7 +252,7 @@ effectTest( const divergence = yield* Effect.flip( restarted.replace({ ...emptyRebuild, documents: [staleDocument] }), ); - assert.equal(divergence._tag, 'CoreSearchProjectionInvalid'); + assert.ok(Predicate.isTagged(divergence, 'CoreSearchProjectionInvalid')); yield* restarted.apply({ document: { ...staleDocument, projectionVersion: '3' }, kind: 'upsert', diff --git a/app/packages/core-runtime/tests/unit/action-collector.test.ts b/app/packages/core-runtime/tests/unit/action-collector.test.ts index 71fa21527..445eed263 100644 --- a/app/packages/core-runtime/tests/unit/action-collector.test.ts +++ b/app/packages/core-runtime/tests/unit/action-collector.test.ts @@ -2,7 +2,7 @@ import { runEffectTestPromise } from '@app/core-runtime/testing/effect-runtime'; import assert from 'node:assert/strict'; // @effect-diagnostics asyncFunction:off -- Existing compatibility boundary; expires: 2026-12-31. import test from 'node:test'; -import { Effect, Schema } from 'effect'; +import { Effect, Schema, Predicate } from 'effect'; import { createActionCollector } from '../../src/actions/collector.ts'; const event = (id: string) => @@ -85,8 +85,8 @@ void test('rejects orphan and foreign Domain Event references', async () => { Effect.flip(second.addOutboxMessageInput({}, message('counter.project'))), ); - assert.equal(foreignError._tag, 'ActionCollectorError'); - assert.equal(orphanError._tag, 'ActionCollectorError'); + assert.ok(Predicate.isTagged(foreignError, 'ActionCollectorError')); + assert.ok(Predicate.isTagged(orphanError, 'ActionCollectorError')); }); void test('does not expose externally mutable collector arrays or captured payloads', async () => { @@ -148,10 +148,10 @@ void test('captures one immutable JSON audit-evidence object and rejects invalid }).recordAuditEvidence({ checkpoint: 'started' }), ), ); - assert.equal(repeated._tag, 'ActionCollectorError'); - assert.equal(invalid._tag, 'ActionCollectorError'); - assert.equal(undeclared._tag, 'ActionCollectorError'); - assert.equal(missingSchema._tag, 'ActionCollectorError'); + assert.ok(Predicate.isTagged(repeated, 'ActionCollectorError')); + assert.ok(Predicate.isTagged(invalid, 'ActionCollectorError')); + assert.ok(Predicate.isTagged(undeclared, 'ActionCollectorError')); + assert.ok(Predicate.isTagged(missingSchema, 'ActionCollectorError')); }); void test('applies descriptor evidence policy and rejects incompatible evidence', async () => { @@ -171,7 +171,7 @@ void test('applies descriptor evidence policy and rejects incompatible evidence' ), ); - assert.equal(error._tag, 'ActionCollectorError'); + assert.ok(Predicate.isTagged(error, 'ActionCollectorError')); const metadataCollector = makeCollector(); await runEffectTestPromise( @@ -207,7 +207,7 @@ void test('rejects an Outbox producer that differs from its registered Domain Ev ), ); - assert.equal(error._tag, 'ActionCollectorError'); + assert.ok(Predicate.isTagged(error, 'ActionCollectorError')); }); void test('enforces Action-declared event payloads and producer ownership', async () => { @@ -245,9 +245,9 @@ void test('enforces Action-declared event payloads and producer ownership', asyn ), ); - assert.equal(invalidPayload._tag, 'ActionCollectorError'); - assert.equal(invalidProducer._tag, 'ActionCollectorError'); - assert.equal(undeclared._tag, 'ActionCollectorError'); - assert.equal(inheritedName._tag, 'ActionCollectorError'); + assert.ok(Predicate.isTagged(invalidPayload, 'ActionCollectorError')); + assert.ok(Predicate.isTagged(invalidProducer, 'ActionCollectorError')); + assert.ok(Predicate.isTagged(undeclared, 'ActionCollectorError')); + assert.ok(Predicate.isTagged(inheritedName, 'ActionCollectorError')); assert.equal(collector.snapshot().domainEvents.length, 0); }); diff --git a/app/packages/core-runtime/tests/unit/action-definition.test.ts b/app/packages/core-runtime/tests/unit/action-definition.test.ts index 41b624853..aa403c136 100644 --- a/app/packages/core-runtime/tests/unit/action-definition.test.ts +++ b/app/packages/core-runtime/tests/unit/action-definition.test.ts @@ -56,7 +56,7 @@ void test('defines an immutable typed descriptor and decodes typed payloads and assert.equal(Object.isFrozen(registration.descriptor.policies), true); }); -test('keeps the Resource permission resolver private behind an immutable declaration', () => { +void test('keeps the Resource permission resolver private behind an immutable declaration', () => { const permission = defineActionResourcePermission<{ readonly counterpartyId: string }>( ({ counterpartyId }) => ({ permission: 'write', @@ -74,7 +74,7 @@ test('keeps the Resource permission resolver private behind an immutable declara assert.equal('resolver' in permission, false); }); -test('requires trusted Legal Entity scope for a Counterparty permission declaration', () => { +void test('requires trusted Legal Entity scope for a Counterparty permission declaration', () => { const entrypoint = defineTenantModuleEntrypoint({ access: 'write', authorization: { kind: 'action_execution', provisioning: 'tenant_membership_default' }, @@ -102,7 +102,7 @@ test('requires trusted Legal Entity scope for a Counterparty permission declarat ); }); -test('uses Schema.Void for a no-payload Action', async () => { +void test('uses Schema.Void for a no-payload Action', async () => { const registration = defineAction( { accessEvidencePolicy: { captureMode: 'metadata_only', policyKey: 'cache.read.v1' }, @@ -137,7 +137,7 @@ test('uses Schema.Void for a no-payload Action', async () => { ); assert.equal(payload, undefined); - assert.equal(invalid._tag, 'ActionPayloadValidationError'); + assert.ok(Predicate.isTagged(invalid, 'ActionPayloadValidationError')); }); void test('keeps the private handler outside the public Action registration', () => { @@ -175,7 +175,7 @@ void test('rejects invalid declared results through a typed error', async () => Effect.flip(decodeActionResult(Schema.Struct({ id: Schema.String }), { id: 1 })), ); - assert.equal(error._tag, 'ActionResultValidationError'); + assert.ok(Predicate.isTagged(error, 'ActionResultValidationError')); assert.equal(error.code, 'action_result_invalid'); }); diff --git a/app/packages/core-runtime/tests/unit/action-errors.test.ts b/app/packages/core-runtime/tests/unit/action-errors.test.ts index e489d4952..917da6b2b 100644 --- a/app/packages/core-runtime/tests/unit/action-errors.test.ts +++ b/app/packages/core-runtime/tests/unit/action-errors.test.ts @@ -1,6 +1,6 @@ import assert from 'node:assert/strict'; import test from 'node:test'; -import { Schema } from 'effect'; +import { Schema, Predicate } from 'effect'; import { ACTION_CORE_ERROR_TAGS, ActionAlreadyCommitted, @@ -109,10 +109,10 @@ void test('publishes the exhaustive stable Core Action error tags', () => { }), ]; - assert.deepEqual( - errors.map((error) => error._tag), - ACTION_CORE_ERROR_TAGS, - ); + assert.equal(errors.length, ACTION_CORE_ERROR_TAGS.length); + for (const [index, tag] of ACTION_CORE_ERROR_TAGS.entries()) { + assert.ok(Predicate.isTagged(errors[index], tag)); + } for (const error of errors) { assert.equal(error.reason.includes('postgresql://'), false); assert.equal(error.reason.includes('ontos-local-development-key'), false); diff --git a/app/packages/core-runtime/tests/unit/action-policy.test.ts b/app/packages/core-runtime/tests/unit/action-policy.test.ts index 9911f4319..e00e5113e 100644 --- a/app/packages/core-runtime/tests/unit/action-policy.test.ts +++ b/app/packages/core-runtime/tests/unit/action-policy.test.ts @@ -2,7 +2,7 @@ import { runEffectTestPromise } from '@app/core-runtime/testing/effect-runtime'; import assert from 'node:assert/strict'; // @effect-diagnostics asyncFunction:off -- Existing compatibility boundary; expires: 2026-12-31. import test from 'node:test'; -import { Effect } from 'effect'; +import { Effect, Predicate } from 'effect'; import { defineGlobalPolicy, defineMicroverticalPolicy, @@ -84,7 +84,7 @@ void test('evaluates typed allow and safe denial outcomes', async () => { const denial = await runEffectTestPromise(Effect.flip(denied.evaluate(input))); assert.deepEqual(observed, [input]); - assert.equal(denial._tag, 'PolicyDenied'); + assert.ok(Predicate.isTagged(denial, 'PolicyDenied')); assert.equal(denial.reasonCode, 'stock_unavailable'); assert.equal(denial.reason, 'Requested stock is unavailable — retry later'); assert.equal(Object.isFrozen(denial), true); diff --git a/app/packages/core-runtime/tests/unit/action-runtime.test.ts b/app/packages/core-runtime/tests/unit/action-runtime.test.ts index eb353548c..9b5b8da92 100644 --- a/app/packages/core-runtime/tests/unit/action-runtime.test.ts +++ b/app/packages/core-runtime/tests/unit/action-runtime.test.ts @@ -450,7 +450,7 @@ const makeRepositoryFailures = async () => { return { cause, persistenceFailure, transactionFailure }; }; -test('repository constructors retain original causes across Effect Cause propagation', async () => { +void test('repository constructors retain original causes across Effect Cause propagation', async () => { const { cause, persistenceFailure, transactionFailure } = await makeRepositoryFailures(); const propagatedTransaction = await runEffectTestPromise( Effect.flip(Effect.failCause(Cause.fail(transactionFailure))), @@ -467,14 +467,14 @@ test('repository constructors retain original causes across Effect Cause propaga ); }); -test('public error classes expose no retained-cause accessors', () => { +void test('public error classes expose no retained-cause accessors', () => { for (const errorClass of [ActionTransactionError, ActionInvocationPersistenceError]) { assert.equal('withCause' in errorClass, false); assert.equal('causeOf' in errorClass, false); } }); -test('repository causes are absent from reflection, JSON, and Schema encoding', async () => { +void test('repository causes are absent from reflection, JSON, and Schema encoding', async () => { const { persistenceFailure, transactionFailure } = await makeRepositoryFailures(); const publicTransaction = new ActionTransactionError({ code: transactionFailure.code, @@ -502,7 +502,7 @@ test('repository causes are absent from reflection, JSON, and Schema encoding', }); }); -test('repository cause readers reject foreign objects carrying the former cause property', () => { +void test('repository cause readers reject foreign objects carrying the former cause property', () => { const formerCauseProperty = ['ontos', 'Repository', 'Failure', 'Cause'].join(''); const cause = new Error('foreign defect'); const transactionFailure = Object.assign( @@ -574,7 +574,7 @@ const registration = () => }), ); -test('executes the complete stage order with transaction ownership and success evidence', async () => { +void test('executes the complete stage order with transaction ownership and success evidence', async () => { const harness = makeHarness(); const result = await runEffectTestPromise( harness.runtime.runAction({ @@ -612,7 +612,7 @@ test('executes the complete stage order with transaction ownership and success e }); }); -test('hashes the encoded representation of decoded DateTime and Option values', async () => { +void test('hashes the encoded representation of decoded DateTime and Option values', async () => { const occurredAt = '2026-09-07T10:30:00.000Z'; const payloadSchema = Schema.Struct({ note: Schema.OptionFromNullOr(Schema.String), @@ -679,7 +679,7 @@ test('hashes the encoded representation of decoded DateTime and Option values', ); }); -test('uses a resolver-branded recovery only for the exact support-stop Action and still checks permission', async () => { +void test('uses a resolver-branded recovery only for the exact support-stop Action and still checks permission', async () => { const recoveryPrincipal = await runEffectTestPromise( supportRecoveryPrincipalContextResolverFromRepository({ load: () => @@ -743,7 +743,7 @@ test('uses a resolver-branded recovery only for the exact support-stop Action an .pipe(providePrincipalManagementRepository), ), ); - assert.equal(denied._tag, 'ActionPermissionDenied'); + assert.ok(Predicate.isTagged(denied, 'ActionPermissionDenied')); assert.deepEqual(deniedHarness.permissionCounts(), { permissionCheckCount: 1, rejectionCount: 1, @@ -766,7 +766,7 @@ test('uses a resolver-branded recovery only for the exact support-stop Action an .pipe(providePrincipalManagementRepository), ), ); - assert.equal(wrongCheckpoint._tag, 'ActionTrustedContextValidationError'); + assert.ok(Predicate.isTagged(wrongCheckpoint, 'ActionTrustedContextValidationError')); const wrongAction = await runEffectTestPromise( Effect.flip( @@ -778,10 +778,10 @@ test('uses a resolver-branded recovery only for the exact support-stop Action an }), ), ); - assert.equal(wrongAction._tag, 'ActionTrustedContextValidationError'); + assert.ok(Predicate.isTagged(wrongAction, 'ActionTrustedContextValidationError')); }); -test('fails business Actions closed before invocation, permission, Policy, or handler access', async () => { +void test('fails business Actions closed before invocation, permission, Policy, or handler access', async () => { await forEachSequential( ( [ @@ -840,7 +840,7 @@ test('fails business Actions closed before invocation, permission, Policy, or ha }), ), ); - assert.equal(failure._tag, 'ModuleStateDeniedError', state); + assert.ok(Predicate.isTagged(failure, 'ModuleStateDeniedError'), state); assert.equal(handlerCalls, 0); assert.equal(policyCalls, 0); assert.deepEqual(harness.counts(), { @@ -862,7 +862,7 @@ test('fails business Actions closed before invocation, permission, Policy, or ha ); }); -test('distinguishes unavailable early checks and rolls back a denied locked recheck', async () => { +void test('distinguishes unavailable early checks and rolls back a denied locked recheck', async () => { const action = defineAction( { accessEvidencePolicy: { captureMode: 'metadata_only', policyKey: 'stock.read.v1' }, @@ -899,7 +899,7 @@ test('distinguishes unavailable early checks and rolls back a denied locked rech }), ), ); - assert.equal(unavailableFailure._tag, 'ModuleStateCheckUnavailableError'); + assert.ok(Predicate.isTagged(unavailableFailure, 'ModuleStateCheckUnavailableError')); assert.equal(unavailable.counts().createCount, 0); const locked = makeHarness({ lockedModuleState: 'denied' }); @@ -913,7 +913,7 @@ test('distinguishes unavailable early checks and rolls back a denied locked rech }), ), ); - assert.equal(lockedFailure._tag, 'ModuleStateDeniedError'); + assert.ok(Predicate.isTagged(lockedFailure, 'ModuleStateDeniedError')); assert.deepEqual(locked.gateCounts(), { handlerResolutionCount: 0, moduleStateReadCount: 1, @@ -927,7 +927,7 @@ test('distinguishes unavailable early checks and rolls back a denied locked rech }); }); -test('allows an explicitly authorized Action before Policy evaluation', async () => { +void test('allows an explicitly authorized Action before Policy evaluation', async () => { const harness = makeHarness({ permissionDecision: 'allowed' }); const result = await runEffectTestPromise( harness.runtime.runAction({ @@ -946,7 +946,7 @@ test('allows an explicitly authorized Action before Policy evaluation', async () assert.equal(harness.counts().transactionCount, 1); }); -test('requires a declared tenant role independently from the Action executor relation', async () => { +void test('requires a declared tenant role independently from the Action executor relation', async () => { const tenantAuthorizedRegistration = defineAction( { accessEvidencePolicy: { captureMode: 'metadata_only', policyKey: 'identity.read.v1' }, @@ -993,7 +993,7 @@ test('requires a declared tenant role independently from the Action executor rel }), ), ); - assert.equal(failure._tag, expectedTag); + assert.ok(Predicate.isTagged(failure, expectedTag)); assert.equal(harness.counts().transitionCount, 0); }, ); @@ -1013,7 +1013,7 @@ test('requires a declared tenant role independently from the Action executor rel assert.equal(allowed.counts().transitionCount, 1); }); -test('accepts every Party write authority as an explicit tenant permission', async () => { +void test('accepts every Party write authority as an explicit tenant permission', async () => { const partyPermissions = [ 'manage_party_identity', 'manage_party_relationships', @@ -1073,7 +1073,7 @@ test('accepts every Party write authority as an explicit tenant permission', asy ); }); -test('canonicalizes every resolved tenant permission target for hash and evidence', async () => { +void test('canonicalizes every resolved tenant permission target for hash and evidence', async () => { const action = defineAction( { accessEvidencePolicy: { captureMode: 'metadata_only', policyKey: 'identity.read.v1' }, @@ -1136,7 +1136,7 @@ test('canonicalizes every resolved tenant permission target for hash and evidenc assert.deepEqual(second.flushed[0]?.transport, first.flushed[0]?.transport); }); -test('authorizes Counterparty creation against the trusted Legal Entity before Policy and transaction', async () => { +void test('authorizes Counterparty creation against the trusted Legal Entity before Policy and transaction', async () => { let handlerCalls = 0; let policyCalls = 0; const action = defineAction( @@ -1194,7 +1194,7 @@ test('authorizes Counterparty creation against the trusted Legal Entity before P }), ), ); - assert.equal(failure._tag, 'ActionPermissionDenied'); + assert.ok(Predicate.isTagged(failure, 'ActionPermissionDenied')); assert.equal(policyCalls, 0); assert.equal(handlerCalls, 0); assert.equal(denied.counts().transactionCount, 0); @@ -1223,7 +1223,7 @@ test('authorizes Counterparty creation against the trusted Legal Entity before P }), ), ); - assert.equal(unavailableFailure._tag, 'ActionPermissionCheckError'); + assert.ok(Predicate.isTagged(unavailableFailure, 'ActionPermissionCheckError')); assert.equal(unavailable.rejections.length, 0); assert.equal(unavailable.counts().transactionCount, 0); assert.equal(unavailable.stages.includes('permission_checked'), false); @@ -1251,7 +1251,7 @@ test('authorizes Counterparty creation against the trusted Legal Entity before P ); }); -test('authorizes the resolved Resource target before Policy, transaction, and handler', async () => { +void test('authorizes the resolved Resource target before Policy, transaction, and handler', async () => { let handlerCalls = 0; let policyCalls = 0; const action = defineAction( @@ -1325,7 +1325,7 @@ test('authorizes the resolved Resource target before Policy, transaction, and ha ), ); - assert.equal(failure._tag, 'ActionPermissionDenied'); + assert.ok(Predicate.isTagged(failure, 'ActionPermissionDenied')); assert.equal(policyCalls, 0); assert.equal(handlerCalls, 0); assert.equal(denied.counts().transactionCount, 0); @@ -1363,12 +1363,12 @@ test('authorizes the resolved Resource target before Policy, transaction, and ha }), ), ); - assert.equal(unavailableFailure._tag, 'ActionPermissionCheckError'); + assert.ok(Predicate.isTagged(unavailableFailure, 'ActionPermissionCheckError')); assert.equal(unavailable.rejections.length, 0); assert.equal(unavailable.counts().transactionCount, 0); }); -test('persists a definite permission denial before returning it and never evaluates Policies', async () => { +void test('persists a definite permission denial before returning it and never evaluates Policies', async () => { let handlerCount = 0; let policyCount = 0; let serviceFactoryCount = 0; @@ -1424,7 +1424,7 @@ test('persists a definite permission denial before returning it and never evalua ), ); - assert.equal(failure._tag, 'ActionPermissionDenied'); + assert.ok(Predicate.isTagged(failure, 'ActionPermissionDenied')); assert.equal(failure.code, 'action_permission_denied'); assert.equal(handlerCount, 0); assert.equal(policyCount, 0); @@ -1458,7 +1458,7 @@ test('persists a definite permission denial before returning it and never evalua ]); }); -test('fails closed before Policy evaluation when permission cannot be determined', async () => { +void test('fails closed before Policy evaluation when permission cannot be determined', async () => { const harness = makeHarness({ permissionFailure: true }); const failure = await runEffectTestPromise( Effect.flip( @@ -1471,7 +1471,7 @@ test('fails closed before Policy evaluation when permission cannot be determined ), ); - assert.equal(failure._tag, 'ActionPermissionCheckError'); + assert.ok(Predicate.isTagged(failure, 'ActionPermissionCheckError')); assert.deepEqual(harness.permissionCounts(), { permissionCheckCount: 1, rejectionCount: 0, @@ -1491,7 +1491,7 @@ test('fails closed before Policy evaluation when permission cannot be determined ]); }); -test('does not claim permission denial when terminal evidence persistence rolls back', async () => { +void test('does not claim permission denial when terminal evidence persistence rolls back', async () => { const harness = makeHarness({ permissionDecision: 'denied', rejectionFailure: true }); const failure = await runEffectTestPromise( Effect.flip( @@ -1504,7 +1504,7 @@ test('does not claim permission denial when terminal evidence persistence rolls ), ); - assert.equal(failure._tag, 'ActionTransactionError'); + assert.ok(Predicate.isTagged(failure, 'ActionTransactionError')); assert.deepEqual(harness.permissionCounts(), { permissionCheckCount: 1, rejectionCount: 1, @@ -1513,7 +1513,7 @@ test('does not claim permission denial when terminal evidence persistence rolls assert.equal(harness.counts().transactionCount, 0); }); -test('evaluates Policies in order before running and hands allowed checkpoints to success', async () => { +void test('evaluates Policies in order before running and hands allowed checkpoints to success', async () => { const observed: string[] = []; const globalPolicy = defineGlobalPolicy<{ readonly amount: number }>({ evaluate: () => { @@ -1584,7 +1584,7 @@ test('evaluates Policies in order before running and hands allowed checkpoints t ]); }); -test('short-circuits the first Policy denial, finalizes it, and never starts execution', async () => { +void test('short-circuits the first Policy denial, finalizes it, and never starts execution', async () => { const observed: string[] = []; let handlerExecutions = 0; const policies = [ @@ -1650,7 +1650,7 @@ test('short-circuits the first Policy denial, finalizes it, and never starts exe ), ); - assert.equal(denial._tag, 'ActionPolicyDenied'); + assert.ok(Predicate.isTagged(denial, 'ActionPolicyDenied')); assert.equal(denial.policyReasonCode, 'counter_locked'); assert.equal(denial.reason, 'Counter changes are locked — try later'); assert.deepEqual(observed, ['first', 'denied']); @@ -1682,7 +1682,7 @@ test('short-circuits the first Policy denial, finalizes it, and never starts exe assert.equal(harness.flushed.length, 0); }); -test('sanitizes Policy defects and interrupts without finalizing', async () => { +void test('sanitizes Policy defects and interrupts without finalizing', async () => { const evaluators = [() => Effect.die('secret evaluator defect'), () => Effect.interrupt] as const; await forEachSequential( @@ -1732,7 +1732,7 @@ test('sanitizes Policy defects and interrupts without finalizing', async () => { ), ); - assert.equal(error._tag, 'ActionPolicyEvaluationError'); + assert.ok(Predicate.isTagged(error, 'ActionPolicyEvaluationError')); assert.equal(error.reason.includes('secret'), false); assert.equal(handlerExecutions, 0); assert.equal(harness.finalized.length, 0); @@ -1746,7 +1746,7 @@ test('sanitizes Policy defects and interrupts without finalizing', async () => { ); }); -test('returns persistence failure when denial evidence cannot be finalized', async () => { +void test('returns persistence failure when denial evidence cannot be finalized', async () => { let handlerExecutions = 0; const policy = defineGlobalPolicy({ evaluate: () => Effect.fail(denyPolicy('blocked', 'This action is blocked')), @@ -1792,13 +1792,13 @@ test('returns persistence failure when denial evidence cannot be finalized', asy ), ); - assert.equal(error._tag, 'ActionInvocationPersistenceError'); + assert.ok(Predicate.isTagged(error, 'ActionInvocationPersistenceError')); assert.equal(handlerExecutions, 0); assert.equal(harness.finalized.length, 0); assert.equal(harness.counts().transactionCount, 0); }); -test('creates fresh collectors for every execution', async () => { +void test('creates fresh collectors for every execution', async () => { const harness = makeHarness(); await forEachSequential( [ @@ -1825,7 +1825,7 @@ test('creates fresh collectors for every execution', async () => { assert.notEqual(harness.flushed[0]?.evidence, harness.flushed[1]?.evidence); }); -test('evaluates Policies afresh for separate invocations', async () => { +void test('evaluates Policies afresh for separate invocations', async () => { let evaluations = 0; const policy = defineGlobalPolicy<{ readonly amount: number }>({ evaluate: () => { @@ -1880,7 +1880,7 @@ test('evaluates Policies afresh for separate invocations', async () => { assert.equal(evaluations, 2); }); -test('rejects structural payloads, trusted context, and missing idempotency before invocation', async () => { +void test('rejects structural payloads, trusted context, and missing idempotency before invocation', async () => { const harness = makeHarness(); const invalidPayload = await runEffectTestPromise( Effect.flip( @@ -1928,14 +1928,14 @@ test('rejects structural payloads, trusted context, and missing idempotency befo ), ); - assert.equal(invalidPayload._tag, 'ActionPayloadValidationError'); - assert.equal(invalidPrincipal._tag, 'ActionTrustedContextValidationError'); - assert.equal(missingKey._tag, 'ActionIdempotencyKeyRequired'); - assert.equal(forgedSystemPrincipal._tag, 'ActionTrustedContextValidationError'); + assert.ok(Predicate.isTagged(invalidPayload, 'ActionPayloadValidationError')); + assert.ok(Predicate.isTagged(invalidPrincipal, 'ActionTrustedContextValidationError')); + assert.ok(Predicate.isTagged(missingKey, 'ActionIdempotencyKeyRequired')); + assert.ok(Predicate.isTagged(forgedSystemPrincipal, 'ActionTrustedContextValidationError')); assert.equal(harness.counts().createCount, 0); }); -test('preserves declared domain rejections and rolls back collected evidence', async () => { +void test('preserves declared domain rejections and rolls back collected evidence', async () => { const DomainRejectedContract = Schema.TaggedStruct('DomainRejected', { reason: Schema.String, }); @@ -2001,13 +2001,13 @@ test('preserves declared domain rejections and rolls back collected evidence', a ), ); - assert.equal(error._tag, 'DomainRejected'); + assert.ok(Predicate.isTagged(error, 'DomainRejected')); assert.equal(error.reason, 'counter_locked'); assert.equal(policyEvaluations, 1); assert.equal(harness.flushed.length, 0); }); -test('sanitizes unexpected defects and rejects invalid typed results', async () => { +void test('sanitizes unexpected defects and rejects invalid typed results', async () => { const defectHarness = makeHarness(); const defective = defineAction( { @@ -2084,14 +2084,14 @@ test('sanitizes unexpected defects and rejects invalid typed results', async () ), ); - assert.equal(defect._tag, 'ActionHandlerExecutionError'); + assert.ok(Predicate.isTagged(defect, 'ActionHandlerExecutionError')); assert.equal(defect.reason.includes('secret'), false); - assert.equal(resultError._tag, 'ActionResultValidationError'); + assert.ok(Predicate.isTagged(resultError, 'ActionResultValidationError')); assert.equal(defectHarness.flushed.length, 0); assert.equal(resultHarness.flushed.length, 0); }); -test('sanitizes undeclared handler failures instead of widening the domain error contract', async () => { +void test('sanitizes undeclared handler failures instead of widening the domain error contract', async () => { const DeclaredDomainErrorContract = Schema.TaggedStruct('DeclaredDomainError', { reason: Schema.String, }); @@ -2141,12 +2141,12 @@ test('sanitizes undeclared handler failures instead of widening the domain error ), ); - assert.equal(error._tag, 'ActionHandlerExecutionError'); + assert.ok(Predicate.isTagged(error, 'ActionHandlerExecutionError')); assert.equal(error.reason.includes('secret'), false); assert.equal(harness.flushed.length, 0); }); -test('handles committed, conflict, definite rollback, and indeterminate commit branches', async () => { +void test('handles committed, conflict, definite rollback, and indeterminate commit branches', async () => { const committed = makeHarness({ createRecord: { actionInvocationId: 'committed', @@ -2238,23 +2238,23 @@ test('handles committed, conflict, definite rollback, and indeterminate commit b }), ); - assert.equal(committedError._tag, 'ActionAlreadyCommitted'); + assert.ok(Predicate.isTagged(committedError, 'ActionAlreadyCommitted')); assert.equal(committed.counts().transactionCount, 0); assert.equal(committed.permissionCounts().permissionCheckCount, 0); - assert.equal(conflictError._tag, 'ActionRequestHashConflict'); + assert.ok(Predicate.isTagged(conflictError, 'ActionRequestHashConflict')); assert.equal(conflict.counts().transactionCount, 0); assert.equal(conflict.permissionCounts().permissionCheckCount, 0); - assert.equal(definiteError._tag, 'ActionTransactionError'); - assert.equal(definiteCommitError._tag, 'ActionTransactionError'); - assert.equal(uncertainError._tag, 'ActionCommitIndeterminate'); + assert.ok(Predicate.isTagged(definiteError, 'ActionTransactionError')); + assert.ok(Predicate.isTagged(definiteCommitError, 'ActionTransactionError')); + assert.ok(Predicate.isTagged(uncertainError, 'ActionCommitIndeterminate')); assert.equal(uncertain.flushed.length, 1); - assert.deepEqual( - acknowledgementErrors.map((error) => error._tag), - acknowledgementFailureCodes.map(() => 'ActionCommitIndeterminate'), - ); + assert.equal(acknowledgementErrors.length, acknowledgementFailureCodes.length); + for (const error of acknowledgementErrors) { + assert.ok(Predicate.isTagged(error, 'ActionCommitIndeterminate')); + } }); -test('interruption during commit waits for native commit settlement', async () => { +void test('interruption during commit waits for native commit settlement', async () => { const commitStarted = Deferred.makeUnsafe(); const commitSettlement = Deferred.makeUnsafe(); const harness = makeHarness({ @@ -2287,7 +2287,7 @@ test('interruption during commit waits for native commit settlement', async () = assert.equal(harness.flushed.length, 1); }); -test('resolves commit state explicitly and keeps unavailable outcomes indeterminate', async () => { +void test('resolves commit state explicitly and keeps unavailable outcomes indeterminate', async () => { const invocationId = '00000000-0000-4000-8000-000000000099'; const open = makeHarness({ createRecord: { @@ -2330,12 +2330,12 @@ test('resolves commit state explicitly and keeps unavailable outcomes indetermin _tag: 'ActionCommitOpen', invocationId, }); - assert.equal(committedResolution._tag, 'ActionAlreadyCommitted'); - assert.equal(unavailableResolution._tag, 'ActionCommitIndeterminate'); + assert.ok(Predicate.isTagged(committedResolution, 'ActionAlreadyCommitted')); + assert.ok(Predicate.isTagged(unavailableResolution, 'ActionCommitIndeterminate')); assert.equal(unavailableResolution.invocationId, invocationId); }); -test('rejects terminal invocation states before handler execution', async () => { +void test('rejects terminal invocation states before handler execution', async () => { const terminal = makeHarness({ createRecord: { actionInvocationId: 'terminal', @@ -2355,12 +2355,12 @@ test('rejects terminal invocation states before handler execution', async () => ), ); - assert.equal(error._tag, 'ActionInvocationStateError'); + assert.ok(Predicate.isTagged(error, 'ActionInvocationStateError')); assert.equal(terminal.counts().transitionCount, 0); assert.equal(terminal.counts().transactionCount, 0); }); -test('uses one runtime contract for Shell/Core and MicroVertical-shaped registrations', async () => { +void test('uses one runtime contract for Shell/Core and MicroVertical-shaped registrations', async () => { const shell = makeHarness(); const microvertical = makeHarness(); const moduleRegistration = defineAction( @@ -2412,6 +2412,6 @@ test('uses one runtime contract for Shell/Core and MicroVertical-shaped registra assert.deepEqual(moduleResult, { reserved: true }); }); -test('the Core database service identity remains server-only', () => { +void test('the Core database service identity remains server-only', () => { assert.equal(Predicate.isFunction(CoreDatabase), true); }); diff --git a/app/packages/core-runtime/tests/unit/action-testing-harness.test.ts b/app/packages/core-runtime/tests/unit/action-testing-harness.test.ts index b388d83bd..7632d1247 100644 --- a/app/packages/core-runtime/tests/unit/action-testing-harness.test.ts +++ b/app/packages/core-runtime/tests/unit/action-testing-harness.test.ts @@ -2,7 +2,7 @@ import { runEffectTestPromise } from '@app/core-runtime/testing/effect-runtime'; // @effect-diagnostics asyncFunction:off -- Existing compatibility boundary; expires: 2026-12-31. import assert from 'node:assert/strict'; import test from 'node:test'; -import { Effect, Schema } from 'effect'; +import { Effect, Schema, Predicate } from 'effect'; import { defineAction } from '../../src/actions/definition.ts'; import { ACTION_RUNTIME_STAGES } from '../../src/actions/runtime.ts'; import { defineTenantModuleEntrypoint } from '../../src/modules/module-entrypoint.ts'; @@ -65,7 +65,7 @@ const request = { transport: { correlationId: 'action-harness-test', idempotencyKey: 'increment-once' }, } as const; -test('runs the real Action lifecycle and preserves committed replay semantics', async () => { +void test('runs the real Action lifecycle and preserves committed replay semantics', async () => { const harness = makeActionTestHarness({ actionPermission: 'allowed', tenantPermission: 'allowed', @@ -75,7 +75,7 @@ test('runs the real Action lifecycle and preserves committed replay semantics', const replay = await runEffectTestPromise(harness.runtime.runAction(request).pipe(Effect.flip)); const snapshot = harness.snapshot(); - assert.equal(replay._tag, 'ActionAlreadyCommitted'); + assert.ok(Predicate.isTagged(replay, 'ActionAlreadyCommitted')); assert.deepEqual(snapshot.stages.slice(0, ACTION_RUNTIME_STAGES.length), ACTION_RUNTIME_STAGES); assert.equal(snapshot.invocations.length, 1); assert.equal(snapshot.invocations[0]?.status, 'succeeded'); @@ -85,12 +85,12 @@ test('runs the real Action lifecycle and preserves committed replay semantics', assert.equal(snapshot.committed[0]?.evidence.outboxMessages.length, 1); }); -test('defaults authorization closed and never starts a transaction for a denial', async () => { +void test('defaults authorization closed and never starts a transaction for a denial', async () => { const harness = makeActionTestHarness(); const denied = await runEffectTestPromise(harness.runtime.runAction(request).pipe(Effect.flip)); const snapshot = harness.snapshot(); - assert.equal(denied._tag, 'ActionPermissionDenied'); + assert.ok(Predicate.isTagged(denied, 'ActionPermissionDenied')); assert.equal(snapshot.invocations.length, 1); assert.equal(snapshot.invocations[0]?.status, 'rejected'); assert.equal(snapshot.permissionDenials.length, 1); @@ -98,7 +98,7 @@ test('defaults authorization closed and never starts a transaction for a denial' assert.equal(snapshot.stages.includes('handler_executed'), false); }); -test('substitutes typed owner services without replacing the private handler', async () => { +void test('substitutes typed owner services without replacing the private handler', async () => { interface CounterServices { readonly increment: (amount: number) => Effect.Effect; } @@ -156,7 +156,7 @@ test('substitutes typed owner services without replacing the private handler', a assert.equal(harness.snapshot().committed.length, 1); }); -test('rejects missing idempotency before creating an invocation', async () => { +void test('rejects missing idempotency before creating an invocation', async () => { const harness = makeActionTestHarness({ actionPermission: 'allowed', tenantPermission: 'allowed', @@ -172,6 +172,6 @@ test('rejects missing idempotency before creating an invocation', async () => { .pipe(Effect.flip), ); - assert.equal(failure._tag, 'ActionIdempotencyKeyRequired'); + assert.ok(Predicate.isTagged(failure, 'ActionIdempotencyKeyRequired')); assert.equal(harness.snapshot().invocations.length, 0); }); diff --git a/app/packages/core-runtime/tests/unit/commit-recovery-metadata.test.ts b/app/packages/core-runtime/tests/unit/commit-recovery-metadata.test.ts index a4a6b4929..e4413c5ff 100644 --- a/app/packages/core-runtime/tests/unit/commit-recovery-metadata.test.ts +++ b/app/packages/core-runtime/tests/unit/commit-recovery-metadata.test.ts @@ -3,7 +3,7 @@ import { runEffectTestPromise } from '@app/core-runtime/testing/effect-runtime'; // @effect-diagnostics asyncFunction:off -- Existing compatibility boundary; expires: 2026-12-31. import assert from 'node:assert/strict'; import test from 'node:test'; -import { Effect, Schema } from 'effect'; +import { Effect, Schema, Predicate } from 'effect'; import { defineAction } from '../../src/actions/definition.ts'; import { ActionAlreadyCommitted } from '../../src/actions/errors.ts'; import { defineTenantModuleEntrypoint } from '../../src/modules/module-entrypoint.ts'; @@ -17,7 +17,7 @@ const principal = { tenantId: '30000000-0000-4000-8000-000000000001', } as const; -test('committed retry and explicit recovery return the same invocation without rerunning or replaying the result', async () => { +void test('committed retry and explicit recovery return the same invocation without rerunning or replaying the result', async () => { let executions = 0; const registration = defineAction( { @@ -64,7 +64,7 @@ test('committed retry and explicit recovery return the same invocation without r ); for (const outcome of [replay, recovered]) { - assert.equal(outcome._tag, 'ActionAlreadyCommitted'); + assert.ok(Predicate.isTagged(outcome, 'ActionAlreadyCommitted')); assert.equal('invocationId' in outcome ? outcome.invocationId : undefined, invocationId); assert.equal('total' in outcome, false); assert.equal('result' in outcome, false); @@ -74,7 +74,7 @@ test('committed retry and explicit recovery return the same invocation without r assert.equal(harness.snapshot().transactionCount, 1); }); -test('committed error schema requires and preserves the recovery invocation identifier', async () => { +void test('committed error schema requires and preserves the recovery invocation identifier', async () => { const encoded = { _tag: 'ActionAlreadyCommitted', code: 'action_already_committed', @@ -100,7 +100,7 @@ test('committed error schema requires and preserves the recovery invocation iden assert.equal('status' in decoded, false); }); -test('lost commit acknowledgement recovers the committed invocation and faults only once', async () => { +void test('lost commit acknowledgement recovers the committed invocation and faults only once', async () => { let executions = 0; const registration = defineAction( { @@ -144,7 +144,7 @@ test('lost commit acknowledgement recovers the committed invocation and faults o const uncertain = await runEffectTestPromise( harness.runtime.runAction(request).pipe(Effect.flip), ); - assert.equal(uncertain._tag, 'ActionCommitIndeterminate'); + assert.ok(Predicate.isTagged(uncertain, 'ActionCommitIndeterminate')); assert.ok('invocationId' in uncertain); assert.equal(uncertain.invocationId, harness.snapshot().invocations[0]?.actionInvocationId); assert.equal(harness.snapshot().invocations[0]?.status, 'succeeded'); @@ -156,7 +156,7 @@ test('lost commit acknowledgement recovers the committed invocation and faults o ); const replay = await runEffectTestPromise(harness.runtime.runAction(request).pipe(Effect.flip)); for (const outcome of [recovered, replay]) { - assert.equal(outcome._tag, 'ActionAlreadyCommitted'); + assert.ok(Predicate.isTagged(outcome, 'ActionAlreadyCommitted')); assert.ok('invocationId' in outcome); assert.equal(outcome.invocationId, uncertain.invocationId); } diff --git a/app/packages/core-runtime/tests/unit/config.test.ts b/app/packages/core-runtime/tests/unit/config.test.ts index 6dd59706c..5c68dc752 100644 --- a/app/packages/core-runtime/tests/unit/config.test.ts +++ b/app/packages/core-runtime/tests/unit/config.test.ts @@ -2,7 +2,7 @@ import { runEffectTestPromise } from '@app/core-runtime/testing/effect-runtime'; import assert from 'node:assert/strict'; // @effect-diagnostics asyncFunction:off -- Existing compatibility boundary; expires: 2026-12-31. import test from 'node:test'; -import { Effect } from 'effect'; +import { Effect, Predicate } from 'effect'; import { acquirePoolResource } from '../../src/db/client.ts'; import { ROOT_ENV_PATH, @@ -60,8 +60,8 @@ void test('keeps missing and malformed configuration in the typed error channel' ), ); - assert.equal(missing._tag, 'DatabaseConfigError'); - assert.equal(malformed._tag, 'DatabaseConfigError'); + assert.ok(Predicate.isTagged(missing, 'DatabaseConfigError')); + assert.ok(Predicate.isTagged(malformed, 'DatabaseConfigError')); }); void test('requires distinct administrative and least-privilege runtime identities', async () => { @@ -113,10 +113,10 @@ void test('requires distinct administrative and least-privilege runtime identiti assert.equal(valid.runtime.user, 'ontos_runtime'); assert.equal(queryParameterIdentities.admin.user, 'ontos_admin'); assert.equal(queryParameterIdentities.runtime.user, 'ontos_runtime'); - assert.equal(missing._tag, 'DatabaseConfigError'); - assert.equal(identical._tag, 'DatabaseConfigError'); - assert.equal(queryParameterCollision._tag, 'DatabaseConfigError'); - assert.equal(superuserCompatible._tag, 'DatabaseConfigError'); + assert.ok(Predicate.isTagged(missing, 'DatabaseConfigError')); + assert.ok(Predicate.isTagged(identical, 'DatabaseConfigError')); + assert.ok(Predicate.isTagged(queryParameterCollision, 'DatabaseConfigError')); + assert.ok(Predicate.isTagged(superuserCompatible, 'DatabaseConfigError')); }); void test('finalizes the pool resource when its Effect scope closes', async () => { diff --git a/app/packages/core-runtime/tests/unit/database-driver-failure.test.ts b/app/packages/core-runtime/tests/unit/database-driver-failure.test.ts index 81b8194ff..f4e060cad 100644 --- a/app/packages/core-runtime/tests/unit/database-driver-failure.test.ts +++ b/app/packages/core-runtime/tests/unit/database-driver-failure.test.ts @@ -1,5 +1,5 @@ import { EffectDrizzleQueryError } from 'drizzle-orm/effect-core'; -import { Cause, Option, Schema } from 'effect'; +import { Cause, Option, Schema, Predicate } from 'effect'; import { SqlError, UniqueViolation } from 'effect/unstable/sql/SqlError'; import assert from 'node:assert/strict'; import test from 'node:test'; @@ -145,17 +145,17 @@ void test('distinguishes commit ambiguity from definite transaction failures', ( const administrativeShutdown = decodeDatabaseDriverFailure({ code: '57P01' }); const serializationFailure = decodeDatabaseDriverFailure({ code: '40001' }); - assert.equal( - Option.isSome(connectionFailure) && connectionFailure.value._tag, - 'DatabaseCommitAcknowledgementAmbiguous', + assert.ok( + Option.isSome(connectionFailure) && + Predicate.isTagged(connectionFailure.value, 'DatabaseCommitAcknowledgementAmbiguous'), ); - assert.equal( - Option.isSome(administrativeShutdown) && administrativeShutdown.value._tag, - 'DatabaseCommitAcknowledgementAmbiguous', + assert.ok( + Option.isSome(administrativeShutdown) && + Predicate.isTagged(administrativeShutdown.value, 'DatabaseCommitAcknowledgementAmbiguous'), ); - assert.equal( - Option.isSome(serializationFailure) && serializationFailure.value._tag, - 'DatabaseTransactionFailure', + assert.ok( + Option.isSome(serializationFailure) && + Predicate.isTagged(serializationFailure.value, 'DatabaseTransactionFailure'), ); assert.equal(isDatabaseCommitAcknowledgementAmbiguous({ code: '40001' }), false); assert.equal(isDatabaseCommitAcknowledgementAmbiguous({ code: '57014' }), false); diff --git a/app/packages/core-runtime/tests/unit/legal-entity-context.test.ts b/app/packages/core-runtime/tests/unit/legal-entity-context.test.ts index 6dc088ac0..3938c4130 100644 --- a/app/packages/core-runtime/tests/unit/legal-entity-context.test.ts +++ b/app/packages/core-runtime/tests/unit/legal-entity-context.test.ts @@ -1,12 +1,9 @@ import { runEffectTestPromise } from '@app/core-runtime/testing/effect-runtime'; -import { Effect, flow } from 'effect'; +import { Effect, flow, Predicate } from 'effect'; import { ConnectionError, SqlError } from 'effect/unstable/sql/SqlError'; import assert from 'node:assert/strict'; import test from 'node:test'; -import type { - LegalEntityContextError, - LegalEntityContextRecord, -} from '../../src/auth/legal-entity-context.ts'; +import type { LegalEntityContextRecord } from '../../src/auth/legal-entity-context.ts'; import { classifyActiveLegalEntities, classifySelectedLegalEntity, @@ -28,14 +25,6 @@ const activeRecord: LegalEntityContextRecord = { tenantId, }; -const failureTag = (effect: Effect.Effect) => - effect.pipe( - Effect.match({ - onFailure: (error) => error._tag, - onSuccess: () => assert.fail('Expected legal-entity context classification to fail'), - }), - ); - effectTest( 'lists zero, one, and many active legal entities in deterministic safe order', Effect.gen(function* listsActiveLegalEntities() { @@ -94,25 +83,29 @@ effectTest( yield* classifySelectedLegalEntity([activeRecord], tenantId, activeRecord.legalEntityId), { legalEntityId: activeRecord.legalEntityId, legalName: activeRecord.legalName }, ); - assert.equal( - yield* failureTag( - classifySelectedLegalEntity( - [activeRecord], - tenantId, - '20000000-0000-4000-8000-000000000099', + assert.ok( + Predicate.isTagged( + yield* Effect.flip( + classifySelectedLegalEntity( + [activeRecord], + tenantId, + '20000000-0000-4000-8000-000000000099', + ), ), + 'LegalEntityContextMissingError', ), - 'LegalEntityContextMissingError', ); - assert.equal( - yield* failureTag( - classifySelectedLegalEntity( - [{ ...activeRecord, status: 'suspended' }], - tenantId, - activeRecord.legalEntityId, + assert.ok( + Predicate.isTagged( + yield* Effect.flip( + classifySelectedLegalEntity( + [{ ...activeRecord, status: 'suspended' }], + tenantId, + activeRecord.legalEntityId, + ), ), + 'LegalEntityContextInactiveError', ), - 'LegalEntityContextInactiveError', ); }), ); @@ -120,24 +113,32 @@ effectTest( effectTest( 'rejects cross-tenant, malformed, and duplicate records', Effect.gen(function* rejectsInvalidLegalEntityRecords() { - assert.equal( - yield* failureTag( - classifyActiveLegalEntities( - [{ ...activeRecord, tenantId: '10000000-0000-4000-8000-000000000002' }], - tenantId, + assert.ok( + Predicate.isTagged( + yield* Effect.flip( + classifyActiveLegalEntities( + [{ ...activeRecord, tenantId: '10000000-0000-4000-8000-000000000002' }], + tenantId, + ), ), + 'LegalEntityContextInvalidError', ), - 'LegalEntityContextInvalidError', ); - assert.equal( - yield* failureTag( - classifyActiveLegalEntities([{ ...activeRecord, legalName: '' }], tenantId), + assert.ok( + Predicate.isTagged( + yield* Effect.flip( + classifyActiveLegalEntities([{ ...activeRecord, legalName: '' }], tenantId), + ), + 'LegalEntityContextInvalidError', ), - 'LegalEntityContextInvalidError', ); - assert.equal( - yield* failureTag(classifyActiveLegalEntities([activeRecord, { ...activeRecord }], tenantId)), - 'LegalEntityContextAmbiguousError', + assert.ok( + Predicate.isTagged( + yield* Effect.flip( + classifyActiveLegalEntities([activeRecord, { ...activeRecord }], tenantId), + ), + 'LegalEntityContextAmbiguousError', + ), ); }), ); @@ -155,7 +156,7 @@ effectTest( ), }); const error = yield* Effect.flip(context.listActiveForTenant(tenantId)); - assert.equal(error._tag, 'LegalEntityContextUnavailableError'); + assert.ok(Predicate.isTagged(error, 'LegalEntityContextUnavailableError')); assert.doesNotMatch(error.reason, /secret database diagnostic/u); }), ); diff --git a/app/packages/core-runtime/tests/unit/module-state-gate.test.ts b/app/packages/core-runtime/tests/unit/module-state-gate.test.ts index 1a8508555..262834474 100644 --- a/app/packages/core-runtime/tests/unit/module-state-gate.test.ts +++ b/app/packages/core-runtime/tests/unit/module-state-gate.test.ts @@ -226,7 +226,7 @@ void test('deduplicates one batch, reuses an immutable snapshot, and fails undec const failure = await runEffectTestPromise( Effect.flip(checkModuleEntrypoint(snapshot, undeclared)), ); - assert.equal(failure._tag, 'ModuleStateCheckUnavailableError'); + assert.ok(Predicate.isTagged(failure, 'ModuleStateCheckUnavailableError')); const undeclaredSameModule = defineTenantModuleEntrypoint({ access: 'write', authorization: { kind: 'action_execution', provisioning: 'tenant_membership_default' }, @@ -237,7 +237,7 @@ void test('deduplicates one batch, reuses an immutable snapshot, and fails undec const sameModuleFailure = await runEffectTestPromise( Effect.flip(checkModuleEntrypoint(snapshot, undeclaredSameModule)), ); - assert.equal(sameModuleFailure._tag, 'ModuleStateCheckUnavailableError'); + assert.ok(Predicate.isTagged(sameModuleFailure, 'ModuleStateCheckUnavailableError')); assert.equal(reads, 1); }); @@ -334,7 +334,7 @@ void test('the gateway rejects missing trusted principal context before state ac const failure = await runEffectTestPromise( Effect.flip(makeModuleEntrypointGateway(gate).prepareSnapshotInput({}, [descriptor])), ); - assert.equal(failure._tag, 'ModuleStateCheckUnavailableError'); + assert.ok(Predicate.isTagged(failure, 'ModuleStateCheckUnavailableError')); assert.equal(reads, 0); }); @@ -476,7 +476,7 @@ void test('gates every future entrypoint category before its fake implementation denied.map(async (entrypoint) => await runEffectTestPromise(Effect.flip(run(entrypoint)))), ); for (const failure of deniedFailures) { - assert.equal(failure._tag, 'ModuleStateDeniedError'); + assert.ok(Predicate.isTagged(failure, 'ModuleStateDeniedError')); } assert.equal(authorizationCalls, allowed.length); assert.equal(loadCalls, allowed.length); @@ -522,7 +522,7 @@ void test('the gateway never evaluates authorization or lazy implementation on d }), ), ); - assert.equal(failure._tag, 'ModuleStateDeniedError'); + assert.ok(Predicate.isTagged(failure, 'ModuleStateDeniedError')); assert.equal(authorizationCalls, 0); assert.equal(loadFactoryCalls, 0); assert.equal(loadCalls, 0); @@ -540,5 +540,5 @@ void test('a missing row is a definite denial rather than an unavailable read', const failure = await runEffectTestPromise( Effect.flip(checkModuleEntrypoint(snapshot, descriptor)), ); - assert.equal(failure._tag, 'ModuleStateDeniedError'); + assert.ok(Predicate.isTagged(failure, 'ModuleStateDeniedError')); }); diff --git a/app/packages/core-runtime/tests/unit/outbox-health.test.ts b/app/packages/core-runtime/tests/unit/outbox-health.test.ts index 1c9bce66f..2911194d8 100644 --- a/app/packages/core-runtime/tests/unit/outbox-health.test.ts +++ b/app/packages/core-runtime/tests/unit/outbox-health.test.ts @@ -3,13 +3,13 @@ import { runEffectTestPromise } from '@app/core-runtime/testing/effect-runtime'; // @effect-diagnostics asyncFunction:off -- Existing compatibility boundary; expires: 2026-12-31. import assert from 'node:assert/strict'; import test from 'node:test'; -import { ConfigProvider, Effect, Layer, Result } from 'effect'; +import { ConfigProvider, Effect, Layer, Result, Predicate } from 'effect'; import { FetchHttpClient, HttpClient } from 'effect/unstable/http'; import { createOutboxWorkerHealth, serveOutboxWorkerHealth } from '../../src/outbox/health.ts'; import { runOutboxWorkerProcess } from '../../src/outbox/process.ts'; import { OutboxRuntime } from '../../src/outbox/runtime.ts'; -test('production health binds all IPv4 interfaces for external-container probes', async () => +void test('production health binds all IPv4 interfaces for external-container probes', async () => runEffectTestPromise( Effect.scoped( Effect.gen(function* externallyReachableHealth() { @@ -20,7 +20,7 @@ test('production health binds all IPv4 interfaces for external-container probes' ), )); -test('readiness starts false, follows successful/failing cycles, expires, and closes on shutdown', async () => { +void test('readiness starts false, follows successful/failing cycles, expires, and closes on shutdown', async () => { let now = 1000; return runEffectTestPromise( Effect.scoped( @@ -54,7 +54,7 @@ test('readiness starts false, follows successful/failing cycles, expires, and cl ); }); -test('closing the health scope marks it unavailable and releases its dynamically allocated port', async () => +void test('closing the health scope marks it unavailable and releases its dynamically allocated port', async () => runEffectTestPromise( Effect.gen(function* releasedPort() { const health = yield* createOutboxWorkerHealth({ staleAfterMs: 5000 }); @@ -66,7 +66,7 @@ test('closing the health scope marks it unavailable and releases its dynamically }), )); -test('a health port already in use produces a typed server startup failure', async () => +void test('a health port already in use produces a typed server startup failure', async () => runEffectTestPromise( Effect.scoped( Effect.gen(function* occupiedPort() { @@ -76,12 +76,12 @@ test('a health port already in use produces a typed server startup failure', asy Effect.scoped(serveOutboxWorkerHealth(health, { port: server.port })), ); assert.ok(Result.isFailure(result)); - assert.equal(result.failure._tag, 'ServeError'); + assert.ok(Predicate.isTagged(result.failure, 'ServeError')); }), ), )); -test('invalid configured health ports fail startup with a typed configuration error before polling', async () => +void test('invalid configured health ports fail startup with a typed configuration error before polling', async () => runEffectTestPromise( Effect.gen(function* invalidPortConfiguration() { for (const port of ['0', '65536', '4102.5', 'invalid']) { @@ -103,7 +103,7 @@ test('invalid configured health ports fail startup with a typed configuration er ), ); assert.ok(Result.isFailure(result)); - assert.equal(result.failure._tag, 'ConfigError'); + assert.ok(Predicate.isTagged(result.failure, 'ConfigError')); } }), )); diff --git a/app/packages/core-runtime/tests/unit/pool-configuration.test.ts b/app/packages/core-runtime/tests/unit/pool-configuration.test.ts index 1248760f5..3d863f477 100644 --- a/app/packages/core-runtime/tests/unit/pool-configuration.test.ts +++ b/app/packages/core-runtime/tests/unit/pool-configuration.test.ts @@ -1,7 +1,7 @@ import { makeEffectTestCallback } from '@app/core-runtime/testing/effect-runtime'; import assert from 'node:assert/strict'; import test from 'node:test'; -import { Effect, Redacted } from 'effect'; +import { Effect, Redacted, Predicate } from 'effect'; import { DEFAULT_DATABASE_POOL_DEADLINES, configureDatabasePool, @@ -9,7 +9,7 @@ import { const runtimeUrl = 'postgresql://runtime:secret@localhost:5432/ontos'; -test( +void test( 'uses acquisition and statement deadlines without opting into a lock deadline', makeEffectTestCallback( Effect.gen(function* verifyDefaults() { @@ -30,7 +30,7 @@ test( ), ); -test( +void test( 'includes an explicitly opted-in lock deadline', makeEffectTestCallback( Effect.gen(function* verifyLockDeadline() { @@ -44,7 +44,7 @@ test( ), ); -test( +void test( 'rejects URL deadline overrides with a typed configuration failure', makeEffectTestCallback( Effect.forEach( @@ -60,7 +60,7 @@ test( Effect.gen(function* verifyParameter() { const connectionString = Redacted.make(`${runtimeUrl}?${parameter}`); const error = yield* Effect.flip(configureDatabasePool(connectionString)); - assert.equal(error._tag, 'DatabaseConnectionError'); + assert.ok(Predicate.isTagged(error, 'DatabaseConnectionError')); assert.equal( error.reason, 'Database URL deadline parameters and startup options are unsupported; use poolDeadlines', @@ -71,7 +71,7 @@ test( ), ); -test( +void test( 'rejects invalid deadline values with a typed configuration failure', makeEffectTestCallback( Effect.gen(function* verifyInvalidDeadline() { @@ -80,7 +80,7 @@ test( configureDatabasePool(connectionString, { statement_timeout: 0 }), ); - assert.equal(error._tag, 'DatabaseConnectionError'); + assert.ok(Predicate.isTagged(error, 'DatabaseConnectionError')); assert.equal( error.reason, 'Database pool deadlines must be positive 32-bit millisecond integers', diff --git a/app/packages/core-runtime/tests/unit/principal-management.test.ts b/app/packages/core-runtime/tests/unit/principal-management.test.ts index 962c4df8f..d7d182e56 100644 --- a/app/packages/core-runtime/tests/unit/principal-management.test.ts +++ b/app/packages/core-runtime/tests/unit/principal-management.test.ts @@ -1,7 +1,7 @@ import { runEffectTestPromise } from '@app/core-runtime/testing/effect-runtime'; import assert from 'node:assert/strict'; import test from 'node:test'; -import { Effect, Option, flow } from 'effect'; +import { Effect, Option, flow, Predicate } from 'effect'; import type { PrincipalManagementPersistence, PrincipalManagementRepositoryService, @@ -92,8 +92,8 @@ effectTest( }).pipe(provideRepository(transaction)), ); - assert.equal(principalError._tag, 'IdentityTargetInvalidError'); - assert.equal(bindingError._tag, 'IdentityTargetInvalidError'); + assert.ok(Predicate.isTagged(principalError, 'IdentityTargetInvalidError')); + assert.ok(Predicate.isTagged(bindingError, 'IdentityTargetInvalidError')); }), ); @@ -157,9 +157,9 @@ effectTest( ), ); - assert.equal(conflictError._tag, 'IdentityLifecycleConflictError'); - assert.equal(terminalError._tag, 'IdentityLifecycleConflictError'); - assert.equal(reasonError._tag, 'IdentityTargetInvalidError'); + assert.ok(Predicate.isTagged(conflictError, 'IdentityLifecycleConflictError')); + assert.ok(Predicate.isTagged(terminalError, 'IdentityLifecycleConflictError')); + assert.ok(Predicate.isTagged(reasonError, 'IdentityTargetInvalidError')); }), ); @@ -183,7 +183,7 @@ effectTest( tenantId, }).pipe(provideRepository(selectingBinding(record))), ); - assert.equal(error._tag, 'IdentityTargetInvalidError'); + assert.ok(Predicate.isTagged(error, 'IdentityTargetInvalidError')); }), ), ); @@ -234,7 +234,7 @@ effectTest( }).pipe(provideRepository(transaction)), ); - assert.equal(error._tag, 'IdentityLifecycleConflictError'); + assert.ok(Predicate.isTagged(error, 'IdentityLifecycleConflictError')); }), ); @@ -260,7 +260,7 @@ effectTest( ), ); - assert.equal(error._tag, 'IdentityTargetInvalidError'); + assert.ok(Predicate.isTagged(error, 'IdentityTargetInvalidError')); yield* validateSupportImpersonation({ ...input, diff --git a/app/packages/core-runtime/tests/unit/principal-resolver.test.ts b/app/packages/core-runtime/tests/unit/principal-resolver.test.ts index 9a0e5ecb9..1a9dc72fb 100644 --- a/app/packages/core-runtime/tests/unit/principal-resolver.test.ts +++ b/app/packages/core-runtime/tests/unit/principal-resolver.test.ts @@ -1,9 +1,8 @@ import { runEffectTestPromise } from '@app/core-runtime/testing/effect-runtime'; -import { DateTime, Effect, flow } from 'effect'; +import { DateTime, Effect, flow, Predicate } from 'effect'; import { ConnectionError, SqlError } from 'effect/unstable/sql/SqlError'; import assert from 'node:assert/strict'; import test from 'node:test'; -import type { PrincipalResolutionError } from '../../src/auth/principal-resolver-errors.ts'; import type { PrincipalResolutionRecord } from '../../src/auth/principal-resolver.ts'; import { classifyApiKeyPrincipal, @@ -35,14 +34,6 @@ const activeRecord: PrincipalResolutionRecord = { tenantStatus: 'active', }; -const failureTag = (effect: Effect.Effect) => - effect.pipe( - Effect.match({ - onFailure: (error) => error._tag, - onSuccess: () => assert.fail('Expected principal resolution to fail'), - }), - ); - effectTest( 'lists safe eligible tenants by name and tenant ID', Effect.gen(function* listsEligibleTenants() { @@ -162,9 +153,11 @@ effectTest( principalKind: 'human', tenantId: 'tenant-2', }); - assert.equal( - yield* failureTag(classifySelectedPrincipal([activeRecord, selected], 'foreign-tenant')), - 'PrincipalBindingMissingError', + assert.ok( + Predicate.isTagged( + yield* Effect.flip(classifySelectedPrincipal([activeRecord, selected], 'foreign-tenant')), + 'PrincipalBindingMissingError', + ), ); }), ); @@ -175,17 +168,23 @@ effectTest( (['service', 'integration', 'agent', 'system'] as const).map((principalKind) => Effect.gen(function* rejectsNonHumanPrincipal() { const record = { ...activeRecord, principalKind }; - assert.equal( - yield* failureTag(classifyDefaultPrincipal([record])), - 'PrincipalInactiveError', + assert.ok( + Predicate.isTagged( + yield* Effect.flip(classifyDefaultPrincipal([record])), + 'PrincipalInactiveError', + ), ); - assert.equal( - yield* failureTag(classifySelectedPrincipal([record], record.tenantId)), - 'PrincipalInactiveError', + assert.ok( + Predicate.isTagged( + yield* Effect.flip(classifySelectedPrincipal([record], record.tenantId)), + 'PrincipalInactiveError', + ), ); - assert.equal( - yield* failureTag(classifyAvailableTenants([record])), - 'PrincipalInactiveError', + assert.ok( + Predicate.isTagged( + yield* Effect.flip(classifyAvailableTenants([record])), + 'PrincipalInactiveError', + ), ); }), ), @@ -204,11 +203,13 @@ effectTest( }), ), ); - assert.equal( - yield* failureTag( - classifyApiKeyPrincipal([activeRecord, { ...activeRecord, tenantId: 't-2' }]), + assert.ok( + Predicate.isTagged( + yield* Effect.flip( + classifyApiKeyPrincipal([activeRecord, { ...activeRecord, tenantId: 't-2' }]), + ), + 'PrincipalBindingAmbiguousError', ), - 'PrincipalBindingAmbiguousError', ); }), ); @@ -216,40 +217,59 @@ effectTest( effectTest( 'fails closed for empty, inactive, and duplicate eligible resolver states', Effect.gen(function* rejectsInvalidResolverStates() { - assert.equal(yield* failureTag(classifyAvailableTenants([])), 'PrincipalBindingMissingError'); - assert.equal( - yield* failureTag(classifyAvailableTenants([{ ...activeRecord, bindingStatus: 'revoked' }])), - 'PrincipalBindingInactiveError', + assert.ok( + Predicate.isTagged( + yield* Effect.flip(classifyAvailableTenants([])), + 'PrincipalBindingMissingError', + ), ); - assert.equal( - yield* failureTag( - classifyAvailableTenants([ - { - ...activeRecord, - bindingRevokedAt: DateTime.toDateUtc(DateTime.makeUnsafe('2026-03-01T00:00:00.000Z')), - }, - ]), + assert.ok( + Predicate.isTagged( + yield* Effect.flip( + classifyAvailableTenants([{ ...activeRecord, bindingStatus: 'revoked' }]), + ), + 'PrincipalBindingInactiveError', + ), + ); + assert.ok( + Predicate.isTagged( + yield* Effect.flip( + classifyAvailableTenants([ + { + ...activeRecord, + bindingRevokedAt: DateTime.toDateUtc(DateTime.makeUnsafe('2026-03-01T00:00:00.000Z')), + }, + ]), + ), + 'PrincipalBindingInactiveError', ), - 'PrincipalBindingInactiveError', ); - assert.equal( - yield* failureTag( - classifyAvailableTenants([{ ...activeRecord, principalStatus: 'disabled' }]), + assert.ok( + Predicate.isTagged( + yield* Effect.flip( + classifyAvailableTenants([{ ...activeRecord, principalStatus: 'disabled' }]), + ), + 'PrincipalInactiveError', ), - 'PrincipalInactiveError', ); - assert.equal( - yield* failureTag(classifyAvailableTenants([{ ...activeRecord, tenantStatus: 'suspended' }])), - 'TenantInactiveError', + assert.ok( + Predicate.isTagged( + yield* Effect.flip( + classifyAvailableTenants([{ ...activeRecord, tenantStatus: 'suspended' }]), + ), + 'TenantInactiveError', + ), ); - assert.equal( - yield* failureTag( - classifyAvailableTenants([ - activeRecord, - { ...activeRecord, principalId: 'duplicate-principal' }, - ]), + assert.ok( + Predicate.isTagged( + yield* Effect.flip( + classifyAvailableTenants([ + activeRecord, + { ...activeRecord, principalId: 'duplicate-principal' }, + ]), + ), + 'PrincipalBindingAmbiguousError', ), - 'PrincipalBindingAmbiguousError', ); }), ); @@ -268,7 +288,7 @@ effectTest( ), }).listAvailableTenants('subject'), ); - assert.equal(error._tag, 'PrincipalResolverUnavailableError'); + assert.ok(Predicate.isTagged(error, 'PrincipalResolverUnavailableError')); assert.doesNotMatch(error.reason, /secret database error/u); }), ); diff --git a/app/packages/core-runtime/tests/unit/read-runtime.test.ts b/app/packages/core-runtime/tests/unit/read-runtime.test.ts index 7dd1cf610..105951fc5 100644 --- a/app/packages/core-runtime/tests/unit/read-runtime.test.ts +++ b/app/packages/core-runtime/tests/unit/read-runtime.test.ts @@ -256,7 +256,7 @@ void test('uses each denying Policy reference own declared HTTP status', async ( }), ), ); - assert.equal(error._tag, 'ReadPolicyDenied'); + assert.ok(Predicate.isTagged(error, 'ReadPolicyDenied')); assert.equal(Schema.decodeUnknownSync(ReadPolicyDenied)(error).httpStatus, denialStatus); }), ); @@ -322,7 +322,7 @@ void test('rejects invalid input before opening a transaction or executing a han }), ), ); - assert.equal(error._tag, 'ReadInputValidationError'); + assert.ok(Predicate.isTagged(error, 'ReadInputValidationError')); assert.equal(harness.evidence(), 0); }); @@ -355,7 +355,7 @@ void test('preserves typed result-validation failure across transaction rollback }), ), ); - assert.equal(error._tag, 'ReadResultValidationError'); + assert.ok(Predicate.isTagged(error, 'ReadResultValidationError')); assert.equal(harness.evidence(), 0); }); @@ -371,7 +371,7 @@ void test('never releases an allowed result when required evidence persistence f }), ), ); - assert.equal(error._tag, 'ReadEvidencePersistenceError'); + assert.ok(Predicate.isTagged(error, 'ReadEvidencePersistenceError')); assert.equal(harness.evidence(), 0); }); @@ -403,7 +403,7 @@ void test('preserves scoped service-factory unavailability and never invokes the }), ), ); - assert.equal(error._tag, 'OperationContextUnavailable'); + assert.ok(Predicate.isTagged(error, 'OperationContextUnavailable')); assert.equal(handlerCalls, 0); assert.equal(harness.evidence(), 0); }); @@ -447,13 +447,13 @@ void test('persists sanitized permission denial and never invokes the private ha }), ), ); - assert.equal(error._tag, 'ReadPermissionDenied'); + assert.ok(Predicate.isTagged(error, 'ReadPermissionDenied')); assert.deepEqual(legalEntityPermissions, ['read_counterparty']); assert.equal(handlerCalls, 0); assert.equal(harness.evidence(), 1); }); -test('fails closed when explicit Counterparty read authority is unavailable', async () => { +void test('fails closed when explicit Counterparty read authority is unavailable', async () => { const legalEntityId = '00000000-0000-4000-8000-000000000004'; let handlerCalls = 0; const harness = makeHarness({ @@ -490,12 +490,12 @@ test('fails closed when explicit Counterparty read authority is unavailable', as }), ), ); - assert.equal(error._tag, 'ReadPermissionUnavailable'); + assert.ok(Predicate.isTagged(error, 'ReadPermissionUnavailable')); assert.equal(handlerCalls, 0); assert.equal(harness.evidence(), 0); }); -test('derives the authorized resource from decoded input and ignores conflicting transport hints', async () => { +void test('derives the authorized resource from decoded input and ignores conflicting transport hints', async () => { const legalEntityId = '00000000-0000-4000-8000-000000000004'; let authorizedTarget: { moduleId: string; resourceId: string; resourceType: string } | undefined; const harness = makeHarness({ @@ -545,7 +545,7 @@ test('derives the authorized resource from decoded input and ignores conflicting assert.deepEqual(authorizedTarget, target); }); -test('authorizes a canonical Resource through explicit tenant Party administration alternatives', async () => { +void test('authorizes a canonical Resource through explicit tenant Party administration alternatives', async () => { const legalEntityId = '00000000-0000-4000-8000-000000000004'; const target = { moduleId: 'party.registry', @@ -653,7 +653,7 @@ test('authorizes a canonical Resource through explicit tenant Party administrati }), ), ); - assert.equal(unavailable._tag, 'ReadPermissionUnavailable'); + assert.ok(Predicate.isTagged(unavailable, 'ReadPermissionUnavailable')); assert.equal(indeterminate.evidence(), 0); const denied = makeHarness({ @@ -671,12 +671,12 @@ test('authorizes a canonical Resource through explicit tenant Party administrati }), ), ); - assert.equal(denial._tag, 'ReadPermissionDenied'); + assert.ok(Predicate.isTagged(denial, 'ReadPermissionDenied')); assert.equal(denied.evidence(), 1); assert.equal(handlerCalls, 2); }); -test('rejects generic tenant access as an alternative permission target', async () => { +void test('rejects generic tenant access as an alternative permission target', async () => { const legalEntityId = '00000000-0000-4000-8000-000000000004'; let handlerCalls = 0; const invalid = defineRead( @@ -723,11 +723,11 @@ test('rejects generic tenant access as an alternative permission target', async }), ), ); - assert.equal(failure._tag, 'ReadHandlerExecutionError'); + assert.ok(Predicate.isTagged(failure, 'ReadHandlerExecutionError')); assert.equal(handlerCalls, 0); }); -test('never treats missing Legal Entity scope as an allowed alternative', async () => { +void test('never treats missing Legal Entity scope as an allowed alternative', async () => { let handlerCalls = 0; const composed = defineRead( { @@ -757,11 +757,11 @@ test('never treats missing Legal Entity scope as an allowed alternative', async }), ), ); - assert.equal(failure._tag, 'ReadPermissionUnavailable'); + assert.ok(Predicate.isTagged(failure, 'ReadPermissionUnavailable')); assert.equal(handlerCalls, 0); }); -test('rejects alternative targets whenever result authorization cannot preserve them', async () => { +void test('rejects alternative targets whenever result authorization cannot preserve them', async () => { let handlerCalls = 0; const search = defineRead( { @@ -792,11 +792,11 @@ test('rejects alternative targets whenever result authorization cannot preserve }), ), ); - assert.equal(failure._tag, 'ReadHandlerExecutionError'); + assert.ok(Predicate.isTagged(failure, 'ReadHandlerExecutionError')); assert.equal(handlerCalls, 0); }); -test('rejects handler-controlled hashes in metadata-only evidence', async () => { +void test('rejects handler-controlled hashes in metadata-only evidence', async () => { const harness = makeHarness(); const unboundedEvidence = defineRead( registration().descriptor, @@ -814,7 +814,7 @@ test('rejects handler-controlled hashes in metadata-only evidence', async () => }), ), ); - assert.equal(error._tag, 'ReadEvidenceValidationError'); + assert.ok(Predicate.isTagged(error, 'ReadEvidenceValidationError')); assert.equal(harness.evidence(), 0); }); @@ -842,7 +842,7 @@ void test('persists late definite denial after rolling back the owner transactio }), ), ); - assert.equal(error._tag, 'ReadPermissionDenied'); + assert.ok(Predicate.isTagged(error, 'ReadPermissionDenied')); assert.equal(harness.evidence(), 1); }); @@ -885,11 +885,11 @@ void test('does not release generated search candidates denied by result-level a }), ), ); - assert.equal(error._tag, 'ReadPermissionDenied'); + assert.ok(Predicate.isTagged(error, 'ReadPermissionDenied')); assert.equal(harness.evidence(), 1); }); -test('authorizes tenant-scoped Party search results without fabricating a Legal Entity', async () => { +void test('authorizes tenant-scoped Party search results without fabricating a Legal Entity', async () => { const candidate = Schema.decodeUnknownSync(ResourceTargetSchema)({ moduleId: 'party.registry', resourceId: 'party-1', @@ -933,7 +933,7 @@ test('authorizes tenant-scoped Party search results without fabricating a Legal assert.equal(resourceChecks, 0); }); -test('fails closed when tenant-scoped Party result authorization becomes unavailable', async () => { +void test('fails closed when tenant-scoped Party result authorization becomes unavailable', async () => { const candidate = Schema.decodeUnknownSync(ResourceTargetSchema)({ moduleId: 'party.registry', resourceId: 'party-1', @@ -966,10 +966,10 @@ test('fails closed when tenant-scoped Party result authorization becomes unavail }), ), ); - assert.equal(failure._tag, 'ReadPermissionUnavailable'); + assert.ok(Predicate.isTagged(failure, 'ReadPermissionUnavailable')); }); -test('preserves declared owner read availability and not-found failures but sanitizes defects', async () => { +void test('preserves declared owner read availability and not-found failures but sanitizes defects', async () => { const failures = [ new ReadHandlerUnavailable({ code: 'read_handler_unavailable', @@ -1005,7 +1005,9 @@ test('preserves declared owner read availability and not-found failures but sani }), ), ); - assert.equal(error._tag, expectedTags[index]); + const expectedTag = expectedTags[index]; + assert.ok(expectedTag !== undefined); + assert.ok(Predicate.isTagged(error, expectedTag)); assert.doesNotMatch(error.reason, /secret/u); assert.equal(harness.evidence(), 0); }), @@ -1075,7 +1077,7 @@ void test('prioritizes failed denial evidence while retaining permission denial assert.ok(Exit.isFailure(exit)); const failures = exit.cause.reasons.filter(Cause.isFailReason).map((reason) => reason.error); assert.equal(failures.length, 2); - assert.equal(failures[0]?._tag, 'ReadEvidencePersistenceError'); + assert.ok(Predicate.isTagged(failures[0], 'ReadEvidencePersistenceError')); assert.equal(failures[1], denied); - assert.equal(failures[1]._tag, 'ReadPermissionDenied'); + assert.ok(Predicate.isTagged(failures[1], 'ReadPermissionDenied')); }); diff --git a/app/packages/core-runtime/tests/unit/scoped-transaction.test.ts b/app/packages/core-runtime/tests/unit/scoped-transaction.test.ts index 833729e17..d38001d97 100644 --- a/app/packages/core-runtime/tests/unit/scoped-transaction.test.ts +++ b/app/packages/core-runtime/tests/unit/scoped-transaction.test.ts @@ -2,7 +2,7 @@ import { runEffectTestPromise } from '@app/core-runtime/testing/effect-runtime'; // @effect-diagnostics asyncFunction:off -- Existing compatibility boundary; expires: 2026-12-31. import assert from 'node:assert/strict'; import test from 'node:test'; -import { Effect, Option } from 'effect'; +import { Effect, Option, Predicate } from 'effect'; import { OperationalScopeTransaction, installOperationalScopeFromTransactionService, @@ -72,7 +72,7 @@ void test('fails closed when transaction settings do not match', async () => { }).pipe(Effect.provideService(OperationalScopeTransaction, transaction)), ), ); - assert.equal(error._tag, 'OperationContextUnavailable'); + assert.ok(Predicate.isTagged(error, 'OperationContextUnavailable')); }); void test('creates complete CRUD RLS policies with update using and with-check predicates', () => { diff --git a/app/packages/core-runtime/tests/unit/search-ingestion.test.ts b/app/packages/core-runtime/tests/unit/search-ingestion.test.ts index 97aba69fd..375fdae48 100644 --- a/app/packages/core-runtime/tests/unit/search-ingestion.test.ts +++ b/app/packages/core-runtime/tests/unit/search-ingestion.test.ts @@ -1,7 +1,7 @@ import { runEffectTestSync } from '@app/core-runtime/testing/effect-runtime'; import assert from 'node:assert/strict'; import test from 'node:test'; -import { Effect } from 'effect'; +import { Effect, Predicate } from 'effect'; import { CORE_SEARCH_INGESTION_REGISTRATIONS, CORE_SEARCH_PARTY_LIFECYCLE_TOPICS, @@ -104,7 +104,7 @@ effectTest('identifier updates accept only their generated self-consumer worker' workerKey: 'party.registry.project-official-identifier-added-to-search', }), ); - assert.equal(denied._tag, 'CoreSearchProjectionInvalid'); + assert.ok(Predicate.isTagged(denied, 'CoreSearchProjectionInvalid')); }); }); @@ -138,7 +138,7 @@ effectTest('rejects undeclared topics and sequence/document identity mismatches' Effect.tap((failures) => Effect.sync(() => { for (const failure of failures) { - assert.equal(failure._tag, 'CoreSearchProjectionInvalid'); + assert.ok(Predicate.isTagged(failure, 'CoreSearchProjectionInvalid')); } }), ), diff --git a/app/packages/core-runtime/tests/unit/search-projection.test.ts b/app/packages/core-runtime/tests/unit/search-projection.test.ts index 0f5d817d8..d4ddd6630 100644 --- a/app/packages/core-runtime/tests/unit/search-projection.test.ts +++ b/app/packages/core-runtime/tests/unit/search-projection.test.ts @@ -1,7 +1,7 @@ import { runEffectTestSync } from '@app/core-runtime/testing/effect-runtime'; import assert from 'node:assert/strict'; import test from 'node:test'; -import { Effect, Schema } from 'effect'; +import { Effect, Schema, Predicate } from 'effect'; import { makeCoreSearchQueryRuntime, makeInMemoryCoreSearchProjectionStore, @@ -87,7 +87,7 @@ effectTest( ); yield* store.replace(rebuild); const divergent = yield* Effect.flip(store.replace({ ...rebuild, documents: [party()] })); - assert.equal(divergent._tag, 'CoreSearchProjectionInvalid'); + assert.ok(Predicate.isTagged(divergent, 'CoreSearchProjectionInvalid')); yield* store.apply({ document: party({ projectionVersion: '3' }), kind: 'upsert' }); yield* store.replace(rebuild); const searchResults = yield* runtime.search({ @@ -192,7 +192,7 @@ effectTest( Effect.tap((failures) => Effect.sync(() => { for (const failure of failures) { - assert.equal(failure._tag, 'CoreSearchProjectionInvalid'); + assert.ok(Predicate.isTagged(failure, 'CoreSearchProjectionInvalid')); } }), ), @@ -435,7 +435,7 @@ effectTest( tenantId, }), ); - assert.equal(failure._tag, 'CoreSearchProjectionInvalid'); + assert.ok(Predicate.isTagged(failure, 'CoreSearchProjectionInvalid')); const result = yield* runtime.search({ includeArchived: false, moduleId: 'party.registry', diff --git a/app/packages/core-runtime/tests/unit/system-principal-context.test.ts b/app/packages/core-runtime/tests/unit/system-principal-context.test.ts index 69e05a995..3930dd590 100644 --- a/app/packages/core-runtime/tests/unit/system-principal-context.test.ts +++ b/app/packages/core-runtime/tests/unit/system-principal-context.test.ts @@ -1,6 +1,6 @@ import { runEffectTestPromise } from '@app/core-runtime/testing/effect-runtime'; // @effect-diagnostics anyUnknownInErrorContext:off asyncFunction:off -- Existing compatibility boundary; expires: 2026-12-31. -import { Effect, Option, Schema } from 'effect'; +import { Effect, Option, Schema, Predicate } from 'effect'; import assert from 'node:assert/strict'; import test from 'node:test'; import { TrustedPrincipalContextSchema } from '../../src/actions/principal-context.ts'; @@ -80,9 +80,9 @@ void test('rejects forged registrations, unsafe refs, wrong kinds, and inactive ), ); - assert.equal(invalid._tag, 'SystemPrincipalContextInvalidError'); - assert.equal(wrongKind._tag, 'SystemPrincipalContextDeniedError'); - assert.equal(inactive._tag, 'SystemPrincipalContextDeniedError'); + assert.ok(Predicate.isTagged(invalid, 'SystemPrincipalContextInvalidError')); + assert.ok(Predicate.isTagged(wrongKind, 'SystemPrincipalContextDeniedError')); + assert.ok(Predicate.isTagged(inactive, 'SystemPrincipalContextDeniedError')); assert.throws(() => registerSystemWorkload({ jobKey: 'unsafe:key' }), TypeError); }); @@ -106,7 +106,7 @@ void test('permits service principals only when the trusted registration opts in }), ); - assert.equal(denied._tag, 'SystemPrincipalContextDeniedError'); + assert.ok(Predicate.isTagged(denied, 'SystemPrincipalContextDeniedError')); assert.equal(allowed.authMethod, 'system'); }); diff --git a/app/packages/core-runtime/tests/unit/tenant-module-state.test.ts b/app/packages/core-runtime/tests/unit/tenant-module-state.test.ts index 6b189a010..b5e194d89 100644 --- a/app/packages/core-runtime/tests/unit/tenant-module-state.test.ts +++ b/app/packages/core-runtime/tests/unit/tenant-module-state.test.ts @@ -2,7 +2,7 @@ import { runEffectTestPromise } from '@app/core-runtime/testing/effect-runtime'; import assert from 'node:assert/strict'; // @effect-diagnostics asyncFunction:off -- Existing compatibility boundary; expires: 2026-12-31. import test from 'node:test'; -import { Effect, Schema } from 'effect'; +import { Effect, Schema, Predicate } from 'effect'; import { changeTenantModuleStateAction } from '../../src/modules/actions/change-tenant-module-state.action.ts'; import type { InstalledModuleCatalog, OntosModuleDeploymentContract } from '../../src/index.ts'; import { @@ -101,7 +101,7 @@ void test('uses one canonical tenant module state schema', async () => { const failure = await runEffectTestPromise( Effect.flip(Schema.decodeUnknownEffect(TenantModuleStateSchema)('enabled')), ); - assert.equal(failure._tag, 'SchemaError'); + assert.ok(Predicate.isTagged(failure, 'SchemaError')); }); void test('maps only trusted supported authentication methods to history sources', async () => { @@ -118,7 +118,7 @@ void test('maps only trusted supported authentication methods to history sources const unsupported = await runEffectTestPromise( Effect.flip(resolveTenantModuleStateChangeSource('api_key')), ); - assert.equal(unsupported._tag, 'TenantModuleStateUnsupportedChangeSourceError'); + assert.ok(Predicate.isTagged(unsupported, 'TenantModuleStateUnsupportedChangeSourceError')); assert.equal(unsupported.code, 'tenant_module_state_change_source_unsupported'); }); @@ -129,7 +129,7 @@ void test('rejects a no-op transition without changing first-state semantics', a const unchanged = await runEffectTestPromise( Effect.flip(rejectUnchangedTenantModuleState('active', 'active')), ); - assert.equal(unchanged._tag, 'TenantModuleStateUnchangedError'); + assert.ok(Predicate.isTagged(unchanged, 'TenantModuleStateUnchangedError')); assert.equal(unchanged.code, 'tenant_module_state_unchanged'); }); @@ -187,11 +187,11 @@ void test('validates only installed membership and the target module supported s const unknown = await runEffectTestPromise( Effect.flip(validateTenantModuleStateTransition(installed, 'unknown.module', 'active')), ); - assert.equal(unknown._tag, 'TenantModuleStateUnknownModuleError'); + assert.ok(Predicate.isTagged(unknown, 'TenantModuleStateUnknownModuleError')); const unsupported = await runEffectTestPromise( Effect.flip(validateTenantModuleStateTransition(installed, 'property.registry', 'archived')), ); - assert.equal(unsupported._tag, 'TenantModuleStateUnsupportedStateError'); + assert.ok(Predicate.isTagged(unsupported, 'TenantModuleStateUnsupportedStateError')); await runEffectTestPromise( validateTenantModuleStateTransition(installed, 'property.registry', 'active'), ); diff --git a/app/verticals/party-registry/tests/integration/ares-governed.test.ts b/app/verticals/party-registry/tests/integration/ares-governed.test.ts index 5956001de..96776cba5 100644 --- a/app/verticals/party-registry/tests/integration/ares-governed.test.ts +++ b/app/verticals/party-registry/tests/integration/ares-governed.test.ts @@ -20,6 +20,7 @@ import { Option, Redacted, Schema, + Predicate, } from 'effect'; import { FetchHttpClient, HttpClient, HttpClientResponse } from 'effect/unstable/http'; import { SignJWT, exportJWK, generateKeyPair } from 'jose'; @@ -108,7 +109,7 @@ const lookupIco = Schema.decodeUnknownSync(AresSubjectLookupIcoSchema)('27074358 const endPool = (pool: Pool) => pool.end(); const promiseEffect = (operation: () => PromiseLike) => Effect.promise(operation); -test('exported ARES coordinator uses real authorized HTTP commands, canonical persistence and reviewed correction', () => +void test('exported ARES coordinator uses real authorized HTTP commands, canonical persistence and reviewed correction', () => runEffectTestPromise( Effect.scoped( Effect.gen(function* aresGovernedTestEffect() { @@ -373,9 +374,9 @@ test('exported ARES coordinator uses real authorized HTTP commands, canonical pe Effect.result, ), ); - assert.equal( - 'failure' in unconfirmed && unconfirmed.failure._tag, - 'AresApplySelectionInvalid', + assert.ok( + 'failure' in unconfirmed && + Predicate.isTagged(unconfirmed.failure, 'AresApplySelectionInvalid'), ); const afterUnconfirmed = yield* state(); assert.equal( @@ -386,7 +387,7 @@ test('exported ARES coordinator uses real authorized HTTP commands, canonical pe const appliedMessage = yield* Schema.encodeEffect(Schema.fromJsonString(Schema.Unknown))( applied, ); - assert.equal(applied._tag, 'AresApplyCompleted', appliedMessage); + assert.ok(Predicate.isTagged(applied, 'AresApplyCompleted'), appliedMessage); assert.equal(applied.completed.length, 3); const persisted = yield* state(); assert.equal(persisted.claims.length, 1); @@ -410,7 +411,7 @@ test('exported ARES coordinator uses real authorized HTTP commands, canonical pe assert.equal(persisted.core.outbox.length, 4); assert.ok(persisted.core.invocations.every((item) => item.status === 'succeeded')); const replay = yield* runHttpEffect(applyAresObservation(request, { gateway, baseUrl })); - assert.equal(replay._tag, 'AresApplyCompleted'); + assert.ok(Predicate.isTagged(replay, 'AresApplyCompleted')); assert.equal(replay.completed.length, 0); assert.equal(replay.skipped.length, 3); const afterReplay = yield* state(); @@ -423,7 +424,9 @@ test('exported ARES coordinator uses real authorized HTTP commands, canonical pe const denied = yield* runHttpEffect( applyAresObservation(request, { gateway: deniedGateway, baseUrl }).pipe(Effect.result), ); - assert.equal('failure' in denied && denied.failure._tag, 'AresLookupForbiddenProblem'); + assert.ok( + 'failure' in denied && Predicate.isTagged(denied.failure, 'AresLookupForbiddenProblem'), + ); const afterDenied = yield* state(); assert.equal(afterDenied.core.invocations.length, persisted.core.invocations.length); diff --git a/app/verticals/party-registry/tests/integration/governed-identity.test.ts b/app/verticals/party-registry/tests/integration/governed-identity.test.ts index fc6d3e71d..00bcf2048 100644 --- a/app/verticals/party-registry/tests/integration/governed-identity.test.ts +++ b/app/verticals/party-registry/tests/integration/governed-identity.test.ts @@ -14,7 +14,15 @@ import { import { makeLiveOperationFixture } from '@app/core-runtime/testing/actions'; import { and, eq } from 'drizzle-orm'; -import { Effect, Exit, Layer, Exit as NativeExit, Scope as NativeScope, Redacted } from 'effect'; +import { + Effect, + Exit, + Layer, + Exit as NativeExit, + Scope as NativeScope, + Redacted, + Predicate, +} from 'effect'; import assert from 'node:assert/strict'; import { randomUUID } from 'node:crypto'; import test, { after as afterNativeDatabase } from 'node:test'; @@ -82,13 +90,6 @@ const transport = (idempotencyKey = randomUUID()) => ({ idempotencyKey, targetModuleKey: 'party.registry', }); -const tag = (effect: Effect.Effect) => - effect.pipe( - Effect.match({ - onSuccess: () => 'SUCCESS', - onFailure: (error) => error._tag, - }), - ); const readPartyDetail = (partyRef: PartyRef, principal: TrustedPrincipalContext) => ReadRuntime.pipe( Effect.flatMap((runtime) => @@ -103,7 +104,7 @@ const readPartyDetail = (partyRef: PartyRef, principal: TrustedPrincipalContext) const endPool = (pool: Pool) => pool.end(); const promiseEffect = (operation: () => PromiseLike) => Effect.promise(operation); -test('governed Party identity uses real PostgreSQL and SpiceDB for atomic claims, recovery and temporal authorization', () => +void test('governed Party identity uses real PostgreSQL and SpiceDB for atomic claims, recovery and temporal authorization', () => runEffectTestPromise( Effect.scoped( Effect.gen(function* governedIdentityTestEffect() { @@ -250,18 +251,25 @@ test('governed Party identity uses real PostgreSQL and SpiceDB for atomic claims function* verifyCommitRecoveryEffect(value) { const key = randomUUID(); fixture.faultNextTransaction('lost-ack'); - assert.equal(yield* run(create(value, key).pipe(tag)), 'ActionCommitIndeterminate'); + assert.ok( + Predicate.isTagged( + yield* run(create(value, key).pipe(Effect.flip)), + 'ActionCommitIndeterminate', + ), + ); const before = yield* snapshot(); const invocation = before.core.invocations.find((row) => row.idempotencyKey === key); assert.ok(invocation); - assert.equal( - yield* run( - resolveActionCommit({ - invocationId: invocation.actionInvocationId, - principal: fixture.manager, - }).pipe(tag), + assert.ok( + Predicate.isTagged( + yield* run( + resolveActionCommit({ + invocationId: invocation.actionInvocationId, + principal: fixture.manager, + }).pipe(Effect.flip), + ), + 'ActionAlreadyCommitted', ), - 'ActionAlreadyCommitted', ); const recovered = yield* run( ReadRuntime.pipe( @@ -285,31 +293,40 @@ test('governed Party identity uses real PostgreSQL and SpiceDB for atomic claims assert.equal(recoveredResult.decisionRef.resourceId, original.matchDecisionId); assert.equal(recovered.partyRef?.resourceId ?? null, original.partyId); assert.equal(recovered.caseRef?.resourceId ?? null, original.candidateCaseId); - assert.equal(yield* run(create(value, key).pipe(tag)), 'ActionAlreadyCommitted'); + assert.ok( + Predicate.isTagged( + yield* run(create(value, key).pipe(Effect.flip)), + 'ActionAlreadyCommitted', + ), + ); const after = yield* snapshot(); assert.deepEqual(after.partyRows, before.partyRows); assert.deepEqual(after.decisions, before.decisions); assert.deepEqual(after.core.events, before.core.events); assert.deepEqual(after.core.outbox, before.core.outbox); - assert.equal( - yield* run(readPartyDetail(partyRef, fixture.denied).pipe(tag)), - 'ReadPermissionDenied', + assert.ok( + Predicate.isTagged( + yield* run(readPartyDetail(partyRef, fixture.denied).pipe(Effect.flip)), + 'ReadPermissionDenied', + ), ); }, ), { concurrency: 1, discard: true }, ); const beforeDenied = yield* snapshot(); - assert.equal( - yield* run(create(candidate('00006947', { subjectEvidence: [] })).pipe(tag)), - 'PartyEvidenceInsufficient', + assert.ok( + Predicate.isTagged( + yield* run(create(candidate('00006947', { subjectEvidence: [] })).pipe(Effect.flip)), + 'PartyEvidenceInsufficient', + ), ); const afterDenied = yield* snapshot(); assert.deepEqual(afterDenied.partyRows, beforeDenied.partyRows); assert.deepEqual(afterDenied.decisions, beforeDenied.decisions); assert.deepEqual(afterDenied.cases, beforeDenied.cases); fixture.faultNextTransaction('rollback'); - assert.notEqual(yield* run(create(candidate('00006947')).pipe(tag)), 'SUCCESS'); + yield* run(create(candidate('00006947')).pipe(Effect.flip)); const rolledBack = yield* snapshot(); assert.deepEqual(rolledBack.partyRows, beforeDenied.partyRows); assert.deepEqual(rolledBack.assertions, beforeDenied.assertions); @@ -325,19 +342,27 @@ test('governed Party identity uses real PostgreSQL and SpiceDB for atomic claims ); assert.ok(independent.outcome === 'CREATED'); assert.notEqual(independent.partyRef.resourceId, partyRef.resourceId); - assert.equal( - yield* run(readPartyDetail(independent.partyRef, fixture.manager).pipe(tag)), - 'ReadHandlerNotFound', + assert.ok( + Predicate.isTagged( + yield* run(readPartyDetail(independent.partyRef, fixture.manager).pipe(Effect.flip)), + 'ReadHandlerNotFound', + ), ); - assert.equal( - yield* run( - create(candidate('00006947'), randomUUID(), fixture.legalEntityOnly).pipe(tag), + assert.ok( + Predicate.isTagged( + yield* run( + create(candidate('00006947'), randomUUID(), fixture.legalEntityOnly).pipe( + Effect.flip, + ), + ), + 'ActionPermissionDenied', ), - 'ActionPermissionDenied', ); - assert.equal( - yield* run(readPartyDetail(partyRef, fixture.legalEntityOnly).pipe(tag)), - 'ReadPermissionDenied', + assert.ok( + Predicate.isTagged( + yield* run(readPartyDetail(partyRef, fixture.legalEntityOnly).pipe(Effect.flip)), + 'ReadPermissionDenied', + ), ); const searchLayer = PartySearchProjectionGatewayLive.pipe( Layer.provide(CoreSearchQueryRuntimeLive), @@ -356,22 +381,26 @@ test('governed Party identity uses real PostgreSQL and SpiceDB for atomic claims ), Effect.provideContext(searchContext), ); - assert.equal(yield* run(deniedSearch.pipe(tag)), 'ReadPermissionDenied'); - assert.equal( - yield* run( - ReadRuntime.pipe( - Effect.flatMap((runtime) => - runtime.runRead({ - registration: partyMatchDecisionRead, - input: { decisionRef: independent.decisionRef }, - principal: fixture.manager, - transport: { correlationId: randomUUID() }, - }), + assert.ok( + Predicate.isTagged(yield* run(deniedSearch.pipe(Effect.flip)), 'ReadPermissionDenied'), + ); + assert.ok( + Predicate.isTagged( + yield* run( + ReadRuntime.pipe( + Effect.flatMap((runtime) => + runtime.runRead({ + registration: partyMatchDecisionRead, + input: { decisionRef: independent.decisionRef }, + principal: fixture.manager, + transport: { correlationId: randomUUID() }, + }), + ), + Effect.flip, ), - tag, ), + 'ReadHandlerNotFound', ), - 'ReadHandlerNotFound', ); const provenance = { @@ -419,16 +448,13 @@ test('governed Party identity uses real PostgreSQL and SpiceDB for atomic claims 'partyType', 'storedPartyRef', ]); - assert.notEqual( - yield* run( - runAction({ - registration: counterpartyCreateAction, - payload: { partyRef, provenance }, - principal: fixture.manager, - transport: transport(), - }).pipe(tag), - ), - 'SUCCESS', + yield* run( + runAction({ + registration: counterpartyCreateAction, + payload: { partyRef, provenance }, + principal: fixture.manager, + transport: transport(), + }).pipe(Effect.flip), ); yield* fixture.grantResourceAccess( counterpartyRef, @@ -519,13 +545,19 @@ test('governed Party identity uses real PostgreSQL and SpiceDB for atomic claims }), ); // Domain relationships never provision access to Party records. - assert.equal( - yield* run(readPartyDetail(reviewedPerson.partyRef, fixture.legalEntityOnly).pipe(tag)), - 'ReadPermissionDenied', + assert.ok( + Predicate.isTagged( + yield* run( + readPartyDetail(reviewedPerson.partyRef, fixture.legalEntityOnly).pipe(Effect.flip), + ), + 'ReadPermissionDenied', + ), ); - assert.equal( - yield* run(readPartyDetail(partyRef, fixture.legalEntityOnly).pipe(tag)), - 'ReadPermissionDenied', + assert.ok( + Predicate.isTagged( + yield* run(readPartyDetail(partyRef, fixture.legalEntityOnly).pipe(Effect.flip)), + 'ReadPermissionDenied', + ), ); const updatedRelationship = yield* run( runAction({ @@ -623,7 +655,7 @@ test('governed Party identity uses real PostgreSQL and SpiceDB for atomic claims assert.ok(archivedParty.party.archivedAt); const archivedCounterparty = yield* readCounterparty(); assert.equal(archivedCounterparty.party.archived, true); - assert.notEqual(yield* run(counterparty().pipe(tag)), 'SUCCESS'); + yield* run(counterparty().pipe(Effect.flip)); const unarchive = yield* run( runAction({ registration: unarchivePartyAction, diff --git a/app/verticals/party-registry/tests/unit/api-integration-ares-application.test.ts b/app/verticals/party-registry/tests/unit/api-integration-ares-application.test.ts index f36099099..ee1c2558f 100644 --- a/app/verticals/party-registry/tests/unit/api-integration-ares-application.test.ts +++ b/app/verticals/party-registry/tests/unit/api-integration-ares-application.test.ts @@ -2,7 +2,7 @@ import assert from 'node:assert/strict'; import test from 'node:test'; -import { DateTime, Effect, ManagedRuntime, Match, Option, Result, Schema } from 'effect'; +import { DateTime, Effect, ManagedRuntime, Match, Option, Result, Schema, Predicate } from 'effect'; import { TestClock } from 'effect/testing'; import { AresAppliedEvidenceSchema, @@ -210,7 +210,7 @@ const makeReads = (displayName: string | null = null): AresApplyReads => ({ }), }); -test('runs only explicitly selected standard Actions and preserves every result', async () => { +void test('runs only explicitly selected standard Actions and preserves every result', async () => { const calls: string[] = []; const outcome = await runAresEffectTestPromise( applyAresObservation(request, makeInvoker(calls), { gateway, reads: makeReads() }), @@ -220,14 +220,14 @@ test('runs only explicitly selected standard Actions and preserves every result' 'update-party|Bearer signed-gateway-token', 'add-party-official-identifier|Bearer signed-gateway-token', ]); - assert.equal(outcome._tag, 'AresApplyCompleted'); + assert.ok(Predicate.isTagged(outcome, 'AresApplyCompleted')); assert.deepEqual( outcome.completed.map(({ route }) => route), ['PARTY_UPDATE', 'IDENTIFIER_ADD'], ); }); -test('propagates bounded evidence and independent command delivery keys', async () => { +void test('propagates bounded evidence and independent command delivery keys', async () => { const calls: string[] = []; const recorded: { readonly evidenceRef: string | undefined; @@ -272,7 +272,7 @@ test('propagates bounded evidence and independent command delivery keys', async ); }); -test('stops after the first failed Action and returns a typed partial outcome', async () => { +void test('stops after the first failed Action and returns a typed partial outcome', async () => { const calls: string[] = []; const outcome = await runAresEffectTestPromise( applyAresObservation( @@ -295,10 +295,10 @@ test('stops after the first failed Action and returns a typed partial outcome', ['PARTY_UPDATE'], ); assert.equal(partial.failed.route, 'IDENTIFIER_ADD'); - assert.equal(partial.failed.error._tag, 'TestFailure'); + assert.ok(Predicate.isTagged(partial.failed.error, 'TestFailure')); }); -test('resumes a replay after a prior selected fact is already satisfied', async () => { +void test('resumes a replay after a prior selected fact is already satisfied', async () => { const calls: string[] = []; const outcome = await runAresEffectTestPromise( applyAresObservation(request, makeInvoker(calls), { @@ -308,7 +308,7 @@ test('resumes a replay after a prior selected fact is already satisfied', async ); assert.deepEqual(calls, ['add-party-official-identifier|Bearer signed-gateway-token']); - assert.equal(outcome._tag, 'AresApplyCompleted'); + assert.ok(Predicate.isTagged(outcome, 'AresApplyCompleted')); assert.deepEqual(outcome.skipped, [ { fact: 'BUSINESS_NAME', reason: 'ALREADY_SATISFIED', route: 'PARTY_UPDATE' }, ]); @@ -318,7 +318,7 @@ test('resumes a replay after a prior selected fact is already satisfied', async ); }); -test('defers when canonical revision or refreshed evidence changed', async () => { +void test('defers when canonical revision or refreshed evidence changed', async () => { const calls: string[] = []; const revisionRequest: AresApplyRequest = { ...request, @@ -348,12 +348,12 @@ test('defers when canonical revision or refreshed evidence changed', async () => ), ]); - assert.equal(revisionOutcome._tag, 'AresApplyDeferred'); - assert.equal(changedOutcome._tag, 'AresApplyDeferred'); + assert.ok(Predicate.isTagged(revisionOutcome, 'AresApplyDeferred')); + assert.ok(Predicate.isTagged(changedOutcome, 'AresApplyDeferred')); assert.deepEqual(calls, []); }); -test('rejects unconfirmed or observation-mismatched selections before invoking an Action', async () => { +void test('rejects unconfirmed or observation-mismatched selections before invoking an Action', async () => { const calls: string[] = []; const invalidRequests: readonly AresApplyRequest[] = [ { @@ -396,13 +396,13 @@ test('rejects unconfirmed or observation-mismatched selections before invoking a for (const result of results) { assert.equal('failure' in result, true); if ('failure' in result) { - assert.equal(result.failure instanceof AresApplySelectionInvalid, true); + assert.equal(Schema.is(AresApplySelectionInvalid)(result.failure), true); } } assert.deepEqual(calls, []); }); -test('does not accept a different street number as the observed registered address', async () => { +void test('does not accept a different street number as the observed registered address', async () => { const calls: string[] = []; const invalidRequest: AresApplyRequest = { correlationId: request.correlationId, @@ -492,7 +492,7 @@ const correctionSelection: AresApplyRequest['selections'][number] = { route: 'PARTY_CORRECTION', }; -test('review-authorized assertion context returns explicit Correction handoff without a write', async () => { +void test('review-authorized assertion context returns explicit Correction handoff without a write', async () => { const calls: string[] = []; const reads = makeReads('Wrong name'); let reviewed = false; @@ -542,7 +542,7 @@ test('review-authorized assertion context returns explicit Correction handoff wi assert.deepEqual(calls, []); }); -test('governed identifier history supports ICO correction suspicion without claiming the identifier', async () => { +void test('governed identifier history supports ICO correction suspicion without claiming the identifier', async () => { const calls: string[] = []; const [, selection] = request.selections; assert.ok(selection); @@ -588,7 +588,7 @@ test('governed identifier history supports ICO correction suspicion without clai assert.deepEqual(calls, []); }); -test('every governed read and selected Action receives fresh audience-scoped authorization', async () => { +void test('every governed read and selected Action receives fresh audience-scoped authorization', async () => { const tokens: string[] = []; const calls: string[] = []; const delegate = makeReads(); @@ -632,7 +632,7 @@ test('every governed read and selected Action receives fresh audience-scoped aut ]); }); -test('read denial fails before writes and preserves its declared error', async () => { +void test('read denial fails before writes and preserves its declared error', async () => { const calls: string[] = []; const denied = { _tag: 'PartyDetailForbiddenProblem' as const, @@ -651,7 +651,7 @@ test('read denial fails before writes and preserves its declared error', async ( assert.deepEqual(calls, []); }); -test('alias and archived targets never dispatch selected writes', async () => { +void test('alias and archived targets never dispatch selected writes', async () => { await Promise.all( (['ALIAS', 'ARCHIVED'] as const).map(async (kind) => { const calls: string[] = []; @@ -694,7 +694,7 @@ test('alias and archived targets never dispatch selected writes', async () => { ); }); -test('provider revision change alone invalidates the earlier confirmation', async () => { +void test('provider revision change alone invalidates the earlier confirmation', async () => { const calls: string[] = []; const reads = makeReads(); const outcome = await runAresEffectTestPromise( @@ -712,11 +712,11 @@ test('provider revision change alone invalidates the earlier confirmation', asyn }, }), ); - assert.equal(outcome._tag, 'AresApplyDeferred'); + assert.ok(Predicate.isTagged(outcome, 'AresApplyDeferred')); assert.deepEqual(calls, []); }); -test('retry preserves exact command payload and reports required standard recovery', async () => { +void test('retry preserves exact command payload and reports required standard recovery', async () => { const payloads: unknown[] = []; const calls: string[] = []; const delegate = makeInvoker(calls, 'update-party|Bearer signed-gateway-token'); @@ -746,7 +746,7 @@ test('retry preserves exact command payload and reports required standard recove ]); }); -test('failed second Action stops the following supported address and retains prior commit receipt', async () => { +void test('failed second Action stops the following supported address and retains prior commit receipt', async () => { const calls: string[] = []; const address: AresApplyRequest['selections'][number] = { fact: 'REGISTERED_ADDRESS', @@ -795,7 +795,7 @@ test('failed second Action stops the following supported address and retains pri { gateway, reads: makeReads() }, ), ); - assert.equal(outcome._tag, 'AresApplyPartiallyCompleted'); + assert.ok(Predicate.isTagged(outcome, 'AresApplyPartiallyCompleted')); assert.equal(outcome.completed.length, 1); assert.deepEqual(calls, [ 'update-party|Bearer signed-gateway-token', @@ -803,7 +803,7 @@ test('failed second Action stops the following supported address and retains pri ]); }); -test('stale refreshed evidence and missing canonical target cannot execute enrichment', async () => { +void test('stale refreshed evidence and missing canonical target cannot execute enrichment', async () => { const calls: string[] = []; const stale = await runAresEffectTestPromise( applyAresObservation(request, makeInvoker(calls), { @@ -819,7 +819,7 @@ test('stale refreshed evidence and missing canonical target cannot execute enric }, }), ); - assert.equal(stale._tag, 'AresApplyDeferred'); + assert.ok(Predicate.isTagged(stale, 'AresApplyDeferred')); const absent = await runAresEffectTestPromise( applyAresObservation({ ...request, partyRef: null }, makeInvoker(calls), { gateway, @@ -830,7 +830,7 @@ test('stale refreshed evidence and missing canonical target cannot execute enric assert.deepEqual(calls, []); }); -test('fresh identical refresh cannot revive an expired original confirmation', async () => { +void test('fresh identical refresh cannot revive an expired original confirmation', async () => { const calls: string[] = []; const outcome = await runAresEffectTestPromise( applyAresObservation( @@ -855,7 +855,7 @@ test('fresh identical refresh cannot revive an expired original confirmation', a assert.deepEqual(calls, []); }); -test('a correction route is never historical-error evidence by itself', async () => { +void test('a correction route is never historical-error evidence by itself', async () => { const calls: string[] = []; const outcome = await runAresEffectTestPromise( applyAresObservation({ ...request, selections: [correctionSelection] }, makeInvoker(calls), { diff --git a/app/verticals/party-registry/tests/unit/api-integration-command-runtime.test.ts b/app/verticals/party-registry/tests/unit/api-integration-command-runtime.test.ts index 1b1d48410..76e9bb0fb 100644 --- a/app/verticals/party-registry/tests/unit/api-integration-command-runtime.test.ts +++ b/app/verticals/party-registry/tests/unit/api-integration-command-runtime.test.ts @@ -2,7 +2,7 @@ import assert from 'node:assert/strict'; import { randomUUID } from 'node:crypto'; import test from 'node:test'; -import { ConfigProvider, Context, Effect, Layer, Schema } from 'effect'; +import { ConfigProvider, Context, Effect, Layer, Schema, Predicate } from 'effect'; import { ReadRuntime, ReadHandlerNotFound, @@ -273,7 +273,7 @@ const commandRequest = ( }); }; -test('every registered command is mounted and rejects missing structural input or authentication before the lifecycle', async () => { +void test('every registered command is mounted and rejects missing structural input or authentication before the lifecycle', async () => { const assertion = await makeAssertion(); const harness = makeActionTestHarness(); const app = mounted(harness, assertion.environment); @@ -310,10 +310,13 @@ test('every registered command is mounted and rejects missing structural input o assert.match(response.headers.get('content-type') ?? '', /application\/problem\+json/u); const body = await response.json(); assert.equal( - body._tag, - response.status === 400 - ? 'PartyCommandInvalidRequestProblem' - : 'PartyCommandAuthenticationProblem', + Predicate.isTagged( + body, + response.status === 400 + ? 'PartyCommandInvalidRequestProblem' + : 'PartyCommandAuthenticationProblem', + ), + true, ); assert.equal(body.status, response.status); }, @@ -332,14 +335,14 @@ test('every registered command is mounted and rejects missing structural input o assert.equal(malformed.status, 400); assert.match(malformed.headers.get('content-type') ?? '', /application\/problem\+json/u); const malformedBody = await malformed.json(); - assert.equal(malformedBody._tag, 'PartyCommandInvalidRequestProblem'); + assert.equal(Predicate.isTagged(malformedBody, 'PartyCommandInvalidRequestProblem'), true); assert.equal(harness.snapshot().invocations.length, 0); } finally { await app.dispose(); } }); -test('missing, malformed, and wrong-audience assertions are challenged without creating invocations', async () => { +void test('missing, malformed, and wrong-audience assertions are challenged without creating invocations', async () => { await forEachSequential(['party-registry', 'contacts'], async (audience) => { const assertion = await makeAssertion(audience); const harness = makeActionTestHarness(); @@ -357,7 +360,7 @@ test('missing, malformed, and wrong-audience assertions are challenged without c assert.equal(response.headers.get('www-authenticate'), 'Bearer'); assert.match(response.headers.get('content-type') ?? '', /application\/problem\+json/u); const body = await response.json(); - assert.equal(body._tag, 'PartyCommandAuthenticationProblem'); + assert.equal(Predicate.isTagged(body, 'PartyCommandAuthenticationProblem'), true); assert.equal(body.status, 401); assert.equal(JSON.stringify(body).includes(assertion.token), false); }); @@ -368,7 +371,7 @@ test('missing, malformed, and wrong-audience assertions are challenged without c }); }); -test('verification configuration unavailability is retryable and never reaches the lifecycle', async () => { +void test('verification configuration unavailability is retryable and never reaches the lifecycle', async () => { const assertion = await makeAssertion(); const harness = makeActionTestHarness(); const app = mounted(harness, {}); @@ -382,7 +385,7 @@ test('verification configuration unavailability is retryable and never reaches t assert.equal(response.status, 503); assert.equal(response.headers.get('www-authenticate'), null); const body = await response.json(); - assert.equal(body._tag, 'PartyCommandUnavailableProblem'); + assert.equal(Predicate.isTagged(body, 'PartyCommandUnavailableProblem'), true); assert.equal(body.retryable, true); assert.equal(harness.snapshot().invocations.length, 0); } finally { @@ -390,7 +393,7 @@ test('verification configuration unavailability is retryable and never reaches t } }); -test('correlation and idempotency are mandatory before the Core Action lifecycle', async () => { +void test('correlation and idempotency are mandatory before the Core Action lifecycle', async () => { const assertion = await makeAssertion(); const harness = makeActionTestHarness(); const app = mounted(harness, assertion.environment); @@ -401,7 +404,10 @@ test('correlation and idempotency are mandatory before the Core Action lifecycle ); assert.equal(missingKey.status, 428); const missingKeyBody = await missingKey.json(); - assert.equal(missingKeyBody._tag, 'PartyCommandPreconditionRequiredProblem'); + assert.equal( + Predicate.isTagged(missingKeyBody, 'PartyCommandPreconditionRequiredProblem'), + true, + ); const missingCorrelation = await handle( app, commandRequest('request-search-rebuild', {}, assertion.token, { @@ -411,14 +417,17 @@ test('correlation and idempotency are mandatory before the Core Action lifecycle ); assert.equal(missingCorrelation.status, 400); const missingCorrelationBody = await missingCorrelation.json(); - assert.equal(missingCorrelationBody._tag, 'PartyCommandInvalidRequestProblem'); + assert.equal( + Predicate.isTagged(missingCorrelationBody, 'PartyCommandInvalidRequestProblem'), + true, + ); assert.equal(harness.snapshot().invocations.length, 0); } finally { await app.dispose(); } }); -test('real Core permission denial is a durable 403 and does not execute the command', async () => { +void test('real Core permission denial is a durable 403 and does not execute the command', async () => { const assertion = await makeAssertion(); const harness = makeActionTestHarness({ actionPermission: 'denied', @@ -434,7 +443,7 @@ test('real Core permission denial is a durable 403 and does not execute the comm ); assert.equal(response.status, 403); const body = await response.json(); - assert.equal(body._tag, 'PartyCommandForbiddenProblem'); + assert.equal(Predicate.isTagged(body, 'PartyCommandForbiddenProblem'), true); assert.equal(harness.snapshot().invocations.length, 1); assert.equal(harness.snapshot().permissionDenials.length, 1); } finally { @@ -442,7 +451,7 @@ test('real Core permission denial is a durable 403 and does not execute the comm } }); -test('the real handler translates domain conflicts and rolls back without successful evidence', async () => { +void test('the real handler translates domain conflicts and rolls back without successful evidence', async () => { const assertion = await makeAssertion(); const harness = makeActionTestHarness({ actionPermission: 'allowed', @@ -463,7 +472,7 @@ test('the real handler translates domain conflicts and rolls back without succes ); assert.equal(response.status, 409); const body = await response.json(); - assert.equal(body._tag, 'PartyCommandConflictProblem'); + assert.equal(Predicate.isTagged(body, 'PartyCommandConflictProblem'), true); assert.equal(body.code, 'party_lifecycle_conflict'); assert.equal(harness.snapshot().invocations.length, 1); assert.equal(harness.snapshot().committed.length, 0); @@ -472,7 +481,7 @@ test('the real handler translates domain conflicts and rolls back without succes } }); -test('alias conflicts preserve only safe canonical recovery metadata', async () => { +void test('alias conflicts preserve only safe canonical recovery metadata', async () => { const assertion = await makeAssertion(); const canonicalPartyRef = { ...partyRef, resourceId: 'a4000000-0000-4000-8000-000000000002' }; const harness = makeActionTestHarness({ @@ -502,7 +511,7 @@ test('alias conflicts preserve only safe canonical recovery metadata', async () ); assert.equal(response.status, 409); const body = await response.json(); - assert.equal(body._tag, 'PartyCommandAliasWriteRejectedProblem'); + assert.equal(Predicate.isTagged(body, 'PartyCommandAliasWriteRejectedProblem'), true); assert.deepEqual(body.aliasPartyRef, partyRef); assert.deepEqual(body.canonicalPartyRef, canonicalPartyRef); assert.equal(JSON.stringify(body).includes('Private diagnostic'), false); @@ -512,7 +521,7 @@ test('alias conflicts preserve only safe canonical recovery metadata', async () } }); -test('committed request replay stays a terminal 409 and does not execute or emit twice', async () => { +void test('committed request replay stays a terminal 409 and does not execute or emit twice', async () => { const assertion = await makeAssertion(); const harness = makeActionTestHarness({ actionPermission: 'allowed', @@ -539,7 +548,7 @@ test('committed request replay stays a terminal 409 and does not execute or emit ); assert.equal(replay.status, 409); const body = await replay.json(); - assert.equal(body._tag, 'PartyCommandAlreadyCommittedProblem'); + assert.equal(Predicate.isTagged(body, 'PartyCommandAlreadyCommittedProblem'), true); assert.equal(body.code, 'action_already_committed'); assert.equal(body.invocationId, harness.snapshot().invocations[0]?.actionInvocationId); assert.equal(body.retryCommand, false); @@ -551,7 +560,7 @@ test('committed request replay stays a terminal 409 and does not execute or emit } }); -test('declared not-found, capability-unavailable and unexpected defects retain safe distinct HTTP statuses', async () => { +void test('declared not-found, capability-unavailable and unexpected defects retain safe distinct HTTP statuses', async () => { const assertion = await makeAssertion(); const cases = [ { @@ -599,7 +608,7 @@ test('declared not-found, capability-unavailable and unexpected defects retain s assert.equal(response.status, item.status); assert.match(response.headers.get('content-type') ?? '', /application\/problem\+json/u); const body = await response.json(); - assert.equal(body._tag, item.tag); + assert.equal(Predicate.isTagged(body, item.tag), true); assert.equal(body.status, item.status); assert.equal(JSON.stringify(body).includes('private'), false); if (item.status === 503) { @@ -612,7 +621,7 @@ test('declared not-found, capability-unavailable and unexpected defects retain s }); }); -test('semantically insufficient Party evidence is a declared 422, not a server defect', async () => { +void test('semantically insufficient Party evidence is a declared 422, not a server defect', async () => { const assertion = await makeAssertion(); const harness = makeActionTestHarness({ actionPermission: 'allowed', @@ -648,7 +657,7 @@ test('semantically insufficient Party evidence is a declared 422, not a server d } }); -test('the Core request hash rejects reuse of an idempotency key for a different command payload', async () => { +void test('the Core request hash rejects reuse of an idempotency key for a different command payload', async () => { const assertion = await makeAssertion(); let executions = 0; const harness = makeActionTestHarness({ @@ -699,7 +708,7 @@ test('the Core request hash rejects reuse of an idempotency key for a different } }); -test('commit resolution requires authentication and a valid invocation without creating an Action', async () => { +void test('commit resolution requires authentication and a valid invocation without creating an Action', async () => { const assertion = await makeAssertion(); const harness = makeActionTestHarness(); const app = mounted(harness, assertion.environment); @@ -710,7 +719,7 @@ test('commit resolution requires authentication and a valid invocation without c const malformed = await handle(app, recoveryRequest('not-an-id', assertion.token)); assert.equal(malformed.status, 400); const malformedBody = await malformed.json(); - assert.equal(malformedBody._tag, 'PartyCommandInvalidRequestProblem'); + assert.equal(Predicate.isTagged(malformedBody, 'PartyCommandInvalidRequestProblem'), true); const absent = await handle(app, recoveryRequest(randomUUID(), assertion.token)); assert.equal(absent.status, 404); assert.equal(harness.snapshot().invocations.length, 0); @@ -719,7 +728,7 @@ test('commit resolution requires authentication and a valid invocation without c } }); -test('an open invocation resolves explicitly without authorizing automatic command retry', async () => { +void test('an open invocation resolves explicitly without authorizing automatic command retry', async () => { const assertion = await makeAssertion(); const harness = makeActionTestHarness({ actionPermission: 'allowed', @@ -756,7 +765,7 @@ test('an open invocation resolves explicitly without authorizing automatic comma } }); -test('actual Core commit acknowledgement loss resolves and the mounted governed Read returns the original decision without rerunning the Action', async () => { +void test('actual Core commit acknowledgement loss resolves and the mounted governed Read returns the original decision without rerunning the Action', async () => { const assertion = await makeAssertion(); const decisions = new Map(); let executions = 0; @@ -859,7 +868,7 @@ test('actual Core commit acknowledgement loss resolves and the mounted governed ); assert.equal(uncertain.status, 503); const body = await uncertain.json(); - assert.equal(body._tag, 'PartyCommandCommitIndeterminateProblem'); + assert.equal(Predicate.isTagged(body, 'PartyCommandCommitIndeterminateProblem'), true); assert.equal(body.resolution, 'RESOLVE_COMMIT'); assert.equal(body.retryCommand, false); const invocationId = harness.snapshot().invocations[0]?.actionInvocationId; @@ -892,7 +901,7 @@ test('actual Core commit acknowledgement loss resolves and the mounted governed ); assert.equal(replay.status, 409); const replayBody = await replay.json(); - assert.equal(replayBody._tag, 'PartyCommandAlreadyCommittedProblem'); + assert.equal(Predicate.isTagged(replayBody, 'PartyCommandAlreadyCommittedProblem'), true); assert.equal(replayBody.invocationId, invocationId); assert.equal(replayBody.retryCommand, false); assert.equal(executions, 1); diff --git a/app/verticals/party-registry/tests/unit/ares-lookup-read.test.ts b/app/verticals/party-registry/tests/unit/ares-lookup-read.test.ts index 71addc2c0..3e61cae9c 100644 --- a/app/verticals/party-registry/tests/unit/ares-lookup-read.test.ts +++ b/app/verticals/party-registry/tests/unit/ares-lookup-read.test.ts @@ -3,7 +3,7 @@ import { runEffectTestPromise } from '@app/core-runtime/testing/effect-runtime'; import assert from 'node:assert/strict'; import { readFile, readdir } from 'node:fs/promises'; import test from 'node:test'; -import { Effect, Schema } from 'effect'; +import { Effect, Schema, Predicate } from 'effect'; import { getReadHandler } from '../../../../packages/core-runtime/src/reads/definition.ts'; import { AresLookupApi, @@ -59,7 +59,7 @@ const scope = Object.freeze({ }); const request = Schema.decodeUnknownSync(AresLookupRequestSchema)({ ico: '48039101' }); -test('declares a tenant-authorized Party evidence Read with optional Legal Entity context', () => { +void test('declares a tenant-authorized Party evidence Read with optional Legal Entity context', () => { assert.equal(aresLookupRead.descriptor.accessKind, 'detail'); assert.equal(aresLookupRead.descriptor.legalEntityScope, 'optional'); assert.equal(aresLookupRead.descriptor.permissionTarget, 'tenant'); @@ -68,7 +68,7 @@ test('declares a tenant-authorized Party evidence Read with optional Legal Entit assert.equal(aresLookupRead.descriptor.evidencePolicy.captureMode, 'metadata_only'); }); -test('passes trusted correlation to the private adapter and returns exactly one evidence result', async () => { +void test('passes trusted correlation to the private adapter and returns exactly one evidence result', async () => { const calls: unknown[] = []; const result = await runEffectTestPromise( getReadHandler(aresLookupRead)(request, { @@ -91,7 +91,7 @@ test('passes trusted correlation to the private adapter and returns exactly one assert.deepEqual(result, { evidence: { resultCount: 1 }, result: evidence }); }); -test('maps provider failures to the closed governed Read error vocabulary without leaking details', async () => { +void test('maps provider failures to the closed governed Read error vocabulary without leaking details', async () => { const failures = [ [ new AresSubjectNotFound({ code: 'ares_subject_not_found', reason: 'private 404 body' }), @@ -141,12 +141,12 @@ test('maps provider failures to the closed governed Read error vocabulary withou ), ); for (const { error, expectedTag } of errors) { - assert.equal(error._tag, expectedTag); + assert.ok(Predicate.isTagged(error, expectedTag)); assert.equal(JSON.stringify(error).includes('private'), false); } }); -test('publishes safe status-matched Problem Details and no provider payload schema', () => { +void test('publishes safe status-matched Problem Details and no provider payload schema', () => { interface ProblemFixture { readonly _tag: string; readonly detail: string; @@ -190,7 +190,7 @@ test('publishes safe status-matched Problem Details and no provider payload sche assert.equal(AresLookupApi.identifier, 'AresLookupApi'); }); -test('keeps the ARES integration read-only and exposes no ARES Action', async () => { +void test('keeps the ARES integration read-only and exposes no ARES Action', async () => { const sourceFiles = [ new URL('../../src/integrations/ares/ares-subject.service.ts', import.meta.url), new URL('../../src/api/ares-lookup.read.ts', import.meta.url), diff --git a/app/verticals/party-registry/tests/unit/ares-subject.service.test.ts b/app/verticals/party-registry/tests/unit/ares-subject.service.test.ts index fefd41845..bf1bd0c53 100644 --- a/app/verticals/party-registry/tests/unit/ares-subject.service.test.ts +++ b/app/verticals/party-registry/tests/unit/ares-subject.service.test.ts @@ -2,7 +2,7 @@ import { runEffectTestPromise } from '@app/core-runtime/testing/effect-runtime'; // @effect-diagnostics asyncFunction:off strictEffectProvide:off -- Existing compatibility boundary; expires: 2026-12-31. import assert from 'node:assert/strict'; import test from 'node:test'; -import { DateTime, Effect, Fiber, Layer, Logger, Option } from 'effect'; +import { DateTime, Effect, Fiber, Layer, Logger, Option, Predicate } from 'effect'; import { TestClock } from 'effect/testing'; import { HttpClient, HttpClientError, HttpClientResponse } from 'effect/unstable/http'; import type { HttpClientRequest } from 'effect/unstable/http'; @@ -70,7 +70,7 @@ const capturedLoggerLayer = (entries: string[]) => }), ]); -test('maps a bounded ARES observation and sends an exact credential-free JSON request', async () => { +void test('maps a bounded ARES observation and sends an exact credential-free JSON request', async () => { const requests: { readonly request: HttpClientRequest.HttpClientRequest; readonly url: URL }[] = []; const client = clientFrom((request, url) => { @@ -111,7 +111,7 @@ test('maps a bounded ARES observation and sends an exact credential-free JSON re assert.equal(requests[0]?.request.headers['cookie'], undefined); }); -test('rejects malformed IČOs before provider I/O', async () => { +void test('rejects malformed IČOs before provider I/O', async () => { let requests = 0; const client = clientFrom((request) => { requests += 1; @@ -121,13 +121,13 @@ test('rejects malformed IČOs before provider I/O', async () => { await Promise.all( ['1234567', '123456789', '1234 5678', 'abcdefgh', '../48039101'].map(async (ico) => { const error = await runEffectTestPromise(Effect.flip(lookup(client, ico))); - assert.equal(error._tag, 'AresSubjectInvalidIco'); + assert.ok(Predicate.isTagged(error, 'AresSubjectInvalidIco')); }), ); assert.equal(requests, 0); }); -test('represents absent optional provider facts explicitly without inventing Party facts', async () => { +void test('represents absent optional provider facts explicitly without inventing Party facts', async () => { const client = clientFrom((request) => Effect.succeed( jsonResponse(request, 200, { @@ -152,7 +152,7 @@ test('represents absent optional provider facts explicitly without inventing Par assert.ok(Option.isNone(result.providerRecordRef)); }); -test('keeps not-found, denial, throttling, timeout, and unavailable failures distinct and safe', async () => { +void test('keeps not-found, denial, throttling, timeout, and unavailable failures distinct and safe', async () => { const statusCases = [ [400, 'AresSubjectResponseInvalid', 1], [401, 'AresSubjectDenied', 1], @@ -183,7 +183,7 @@ test('keeps not-found, denial, throttling, timeout, and unavailable failures dis return yield* Fiber.join(fiber); }).pipe(Effect.provide(TestClock.layer())); const error = await runEffectTestPromise(expectedAttempts === 3 ? fiberProgram : program); - assert.equal(error._tag, tag); + assert.ok(Predicate.isTagged(error, tag)); assert.equal(attempts, expectedAttempts); assert.equal(JSON.stringify(error).includes('PRIVATE_PROVIDER_CODE'), false); assert.equal(JSON.stringify(error).includes('private provider detail'), false); @@ -191,7 +191,7 @@ test('keeps not-found, denial, throttling, timeout, and unavailable failures dis ); }); -test('retries transport faults with bounded backoff without exposing diagnostics', async () => { +void test('retries transport faults with bounded backoff without exposing diagnostics', async () => { const logs: string[] = []; let attempts = 0; const client = clientFrom((request) => { @@ -216,14 +216,14 @@ test('retries transport faults with bounded backoff without exposing diagnostics }).pipe(Effect.provide(Layer.mergeAll(TestClock.layer(), capturedLoggerLayer(logs)))); const error = await runEffectTestPromise(program); - assert.equal(error._tag, 'AresSubjectUnavailable'); + assert.ok(Predicate.isTagged(error, 'AresSubjectUnavailable')); assert.equal(attempts, 3); assert.equal(JSON.stringify(error).includes('private socket diagnostic'), false); assert.match(logs.join('\n'), /private socket diagnostic/u); assert.match(logs.join('\n'), /corr private/u); }); -test('times out and aborts each of the three bounded attempts', async () => { +void test('times out and aborts each of the three bounded attempts', async () => { const signals: AbortSignal[] = []; const client = clientFrom((_request, _url, signal) => { signals.push(signal); @@ -237,7 +237,7 @@ test('times out and aborts each of the three bounded attempts', async () => { }).pipe(Effect.provide(TestClock.layer())); const error = await runEffectTestPromise(program); - assert.equal(error._tag, 'AresSubjectTimeout'); + assert.ok(Predicate.isTagged(error, 'AresSubjectTimeout')); assert.equal(signals.length, 3); assert.equal( signals.every((signal) => signal.aborted), @@ -245,7 +245,7 @@ test('times out and aborts each of the three bounded attempts', async () => { ); }); -test('bounds stalled response bodies with the same three-attempt timeout policy', async () => { +void test('bounds stalled response bodies with the same three-attempt timeout policy', async () => { let attempts = 0; const client = clientFrom((request) => { attempts += 1; @@ -268,11 +268,11 @@ test('bounds stalled response bodies with the same three-attempt timeout policy' return yield* Fiber.join(fiber); }).pipe(Effect.provide(TestClock.layer())); const error = await runEffectTestPromise(program); - assert.equal(error._tag, 'AresSubjectTimeout'); + assert.ok(Predicate.isTagged(error, 'AresSubjectTimeout')); assert.equal(attempts, 3); }); -test('rejects malformed JSON, schema drift, mismatched IČO, and oversized text without partial evidence', async () => { +void test('rejects malformed JSON, schema drift, mismatched IČO, and oversized text without partial evidence', async () => { const responses: readonly (( request: HttpClientRequest.HttpClientRequest, ) => HttpClientResponse.HttpClientResponse)[] = [ @@ -290,13 +290,13 @@ test('rejects malformed JSON, schema drift, mismatched IČO, and oversized text return Effect.succeed(response(request)); }); const error = await runEffectTestPromise(Effect.flip(lookup(client))); - assert.equal(error._tag, 'AresSubjectResponseInvalid'); + assert.ok(Predicate.isTagged(error, 'AresSubjectResponseInvalid')); assert.equal(requests, 1); }), ); }); -test('coalesces identical requests and exposes cache age without changing observedAt', async () => { +void test('coalesces identical requests and exposes cache age without changing observedAt', async () => { let requests = 0; const client = clientFrom((request) => { requests += 1; @@ -334,7 +334,7 @@ test('coalesces identical requests and exposes cache age without changing observ assert.notEqual(result.cached.servedAt, result.cached.observedAt); }); -test('bounds distinct upstream lookups to four concurrent requests', async () => { +void test('bounds distinct upstream lookups to four concurrent requests', async () => { let active = 0; let maximumActive = 0; let requests = 0; diff --git a/app/verticals/party-registry/tests/unit/contact-point-persistence.service.test.ts b/app/verticals/party-registry/tests/unit/contact-point-persistence.service.test.ts index ca9ff690e..9a3a84419 100644 --- a/app/verticals/party-registry/tests/unit/contact-point-persistence.service.test.ts +++ b/app/verticals/party-registry/tests/unit/contact-point-persistence.service.test.ts @@ -1,8 +1,8 @@ import { runEffectTestPromise } from '@app/core-runtime/testing/effect-runtime'; /* eslint-disable anti-slop/no-chained-type-assertions, anti-slop/no-unsafe-dictionary-type -- This focused harness models only the Drizzle native Effect query surface exercised by Contact Point ending. expires: 2026-12-31. */ -import { SQL } from 'drizzle-orm'; +import { is, SQL } from 'drizzle-orm'; import { PgDialect } from 'drizzle-orm/pg-core'; -import { DateTime, Effect, Option, Schema } from 'effect'; +import { DateTime, Effect, Option, Schema, Predicate } from 'effect'; import assert from 'node:assert/strict'; import test from 'node:test'; import { AresAppliedEvidenceSchema } from '../../shared/domain/ares-application.ts'; @@ -225,7 +225,7 @@ const wholeEndCommand = (effectiveEnd: string, reason = 'Party retired this mail target: { type: 'WHOLE_CONTACT_POINT' as const }, }); -test('stores future end provenance while keeping the contact current until the boundary', () => +void test('stores future end provenance while keeping the contact current until the boundary', () => runEffectTestPromise( Effect.gen(function* contactPointScenario() { const effectiveEnd = '2099-01-01T00:00:00.000Z'; @@ -264,7 +264,7 @@ test('stores future end provenance while keeping the contact current until the b }), )); -test('stores end provenance on both a last ADDRESS purpose and its owning address', () => +void test('stores end provenance on both a last ADDRESS purpose and its owning address', () => runEffectTestPromise( Effect.gen(function* contactPointScenario() { const effectiveEnd = '2026-02-01T00:00:00.000Z'; @@ -332,7 +332,7 @@ test('stores end provenance on both a last ADDRESS purpose and its owning addres }), )); -test('reuses only an exact end request and rejects changed evidence at the same boundary', () => +void test('reuses only an exact end request and rejects changed evidence at the same boundary', () => runEffectTestPromise( Effect.gen(function* contactPointScenario() { const effectiveEnd = '2026-02-01T00:00:00.000Z'; @@ -366,12 +366,12 @@ test('reuses only an exact end request and rejects changed evidence at the same wholeEndCommand(effectiveEnd, 'A different reason'), ), ); - assert.equal(changed._tag, 'Failure'); + assert.ok(Predicate.isTagged(changed, 'Failure')); assert.equal(changedHarness.updateSets.length, 0); }), )); -test('stores correction end provenance on the preserved original Contact Point', () => +void test('stores correction end provenance on the preserved original Contact Point', () => runEffectTestPromise( Effect.gen(function* contactPointScenario() { const original = contactRow(); @@ -443,7 +443,7 @@ const updateCommand = (change: Parameters[2]['c }, }); -test('re-adds a scheduled-ended purpose as a new period without reopening its history', () => +void test('re-adds a scheduled-ended purpose as a new period without reopening its history', () => runEffectTestPromise( Effect.gen(function* contactPointScenario() { const stalePurpose = purposeRow({ @@ -491,7 +491,7 @@ test('re-adds a scheduled-ended purpose as a new period without reopening its hi }), )); -test('rejects a REGISTERED context collision as a typed domain conflict before mutation', () => +void test('rejects a REGISTERED context collision as a typed domain conflict before mutation', () => runEffectTestPromise( Effect.gen(function* contactPointScenario() { const address = addressRow(); @@ -516,7 +516,7 @@ test('rejects a REGISTERED context collision as a typed domain conflict before m }), ), ); - assert.equal(error._tag, 'PartyContactPointAlreadyExists'); + assert.ok(Predicate.isTagged(error, 'PartyContactPointAlreadyExists')); assert.equal(harness.updateSets.length, 0); assert.equal(harness.insertValues.length, 0); const condition = harness.selectWheres.at(3); @@ -560,13 +560,13 @@ test('rejects a REGISTERED context collision as a typed domain conflict before m verification: { state: 'UNVERIFIED' }, }), ); - assert.equal(addError._tag, 'PartyContactPointAlreadyExists'); + assert.ok(Predicate.isTagged(addError, 'PartyContactPointAlreadyExists')); assert.equal(addHarness.insertValues.length, 0); assert.equal(addHarness.updateSets.length, 0); }), )); -test('advances revisions on both the transferred purpose and its owning address', () => +void test('advances revisions on both the transferred purpose and its owning address', () => runEffectTestPromise( Effect.gen(function* contactPointScenario() { const address = addressRow(); @@ -595,14 +595,14 @@ test('advances revisions on both the transferred purpose and its owning address' assert.equal(harness.updateSets[0]?.['revision'], 8); assert.equal(harness.updateSets[0]?.['preferred'], false); const revision = harness.updateSets[1]?.['revision']; - assert.ok(revision instanceof SQL); + assert.ok(is(revision, SQL)); assert.match(new PgDialect().sqlToQuery(revision).sql, /revision.*\+ 1/u); assert.equal(harness.updateSets[2]?.['revision'], 2); assert.equal(harness.updateSets[3]?.['revision'], 2); }), )); -test('preserves original provenance evidence and appends deduplicated enrichment', () => +void test('preserves original provenance evidence and appends deduplicated enrichment', () => runEffectTestPromise( Effect.gen(function* contactPointScenario() { const row = contactRow({ @@ -641,7 +641,7 @@ test('preserves original provenance evidence and appends deduplicated enrichment }), )); -test('rejects invalid E.164 and oversized extensions through the service typed-error path', () => +void test('rejects invalid E.164 and oversized extensions through the service typed-error path', () => runEffectTestPromise( Effect.all( [ @@ -676,7 +676,7 @@ test('rejects invalid E.164 and oversized extensions through the service typed-e verification: { state: 'UNVERIFIED' }, }), ); - assert.equal(error._tag, 'PartyContactPointInvalid'); + assert.ok(Predicate.isTagged(error, 'PartyContactPointInvalid')); assert.equal(harness.selectWheres.length, 0); assert.equal(harness.insertValues.length, 0); }), @@ -684,7 +684,7 @@ test('rejects invalid E.164 and oversized extensions through the service typed-e ).pipe(Effect.asVoid), )); -test('rejects an explicit alias Party add but keeps durable ContactPoint updates readable through the full chain', () => +void test('rejects an explicit alias Party add but keeps durable ContactPoint updates readable through the full chain', () => runEffectTestPromise( Effect.gen(function* contactPointScenario() { const intermediatePartyId = '20000000-0000-4000-8000-000000000002'; @@ -804,7 +804,7 @@ test('rejects an explicit alias Party add but keeps durable ContactPoint updates }), )); -test('advances the replaced channel preference revision as well as the selected contact', () => +void test('advances the replaced channel preference revision as well as the selected contact', () => runEffectTestPromise( Effect.gen(function* contactPointScenario() { const row = contactRow({ preferred: false }); @@ -821,13 +821,13 @@ test('advances the replaced channel preference revision as well as the selected updateCommand({ preferred: true, type: 'SET_CHANNEL_PREFERRED' }), ); const revision = harness.updateSets[0]?.['revision']; - assert.ok(revision instanceof SQL); + assert.ok(is(revision, SQL)); assert.match(new PgDialect().sqlToQuery(revision).sql, /revision.*\+ 1/u); assert.equal(harness.updateSets[1]?.['revision'], 2); }), )); -test('persists bounded ARES provenance on the address and purpose without using observation time as effective time', () => +void test('persists bounded ARES provenance on the address and purpose without using observation time as effective time', () => runEffectTestPromise( Effect.gen(function* contactPointScenario() { const externalEvidence = yield* Schema.decodeUnknownEffect(AresAppliedEvidenceSchema)({ @@ -906,7 +906,7 @@ test('persists bounded ARES provenance on the address and purpose without using }), )); -test('treats PHONE extensions as distinct endpoints while rejecting an exact duplicate extension', () => +void test('treats PHONE extensions as distinct endpoints while rejecting an exact duplicate extension', () => runEffectTestPromise( Effect.gen(function* contactPointScenario() { const existing = contactRow({ @@ -950,12 +950,12 @@ test('treats PHONE extensions as distinct endpoints while rejecting an exact dup const duplicate = yield* Effect.flip( addContactPointRecord(duplicateHarness.transaction, scope, command('101')), ); - assert.equal(duplicate._tag, 'PartyContactPointAlreadyExists'); + assert.ok(Predicate.isTagged(duplicate, 'PartyContactPointAlreadyExists')); assert.equal(duplicateHarness.insertValues.length, 0); }), )); -test('whole ADDRESS end preserves an earlier purpose end and its independent accepted evidence', () => +void test('whole ADDRESS end preserves an earlier purpose end and its independent accepted evidence', () => runEffectTestPromise( Effect.gen(function* contactPointScenario() { const address = addressRow(); diff --git a/app/verticals/party-registry/tests/unit/correction-contract.test.ts b/app/verticals/party-registry/tests/unit/correction-contract.test.ts index 823c57d50..a20611dca 100644 --- a/app/verticals/party-registry/tests/unit/correction-contract.test.ts +++ b/app/verticals/party-registry/tests/unit/correction-contract.test.ts @@ -1,7 +1,7 @@ import { runEffectTestPromise } from '@app/core-runtime/testing/effect-runtime'; // @effect-diagnostics asyncFunction:off globalDate:off -- Existing compatibility boundary; expires: 2026-12-31. /* eslint-disable anti-slop/no-chained-type-assertions, anti-slop/no-unsafe-dictionary-type -- This harness implements the correction service's Drizzle boundary. expires: 2026-12-31. */ -import { Effect, Match, Option, Schema } from 'effect'; +import { Effect, Match, Option, Schema, Predicate } from 'effect'; import assert from 'node:assert/strict'; import test from 'node:test'; import { @@ -55,7 +55,7 @@ const relationshipCommand = decode(SupersedeRelationshipCorrectionCommandSchema) relationshipCommandEncoded, ); -test('correction is closed to Party type, display name, and official identifier assertions', () => { +void test('correction is closed to Party type, display name, and official identifier assertions', () => { for (const factKind of ['PARTY_TYPE', 'DISPLAY_NAME', 'OFFICIAL_IDENTIFIER']) { assert.doesNotThrow(() => decode(PartyCorrectionCommandSchema)({ @@ -80,7 +80,7 @@ test('correction is closed to Party type, display name, and official identifier ); }); -test('correction follow-up is typed and duplicate confirmation remains readiness-only', () => { +void test('correction follow-up is typed and duplicate confirmation remains readiness-only', () => { assert.equal(classifyCorrectionRoute('PARTY_TYPE'), 'LIFECYCLE_REVIEW'); assert.equal(classifyCorrectionRoute('DISPLAY_NAME'), 'ENRICHMENT_REVIEW'); assert.equal(classifyCorrectionRoute('OFFICIAL_IDENTIFIER'), 'CLAIM_REASSIGNMENT_REVIEW'); @@ -124,7 +124,7 @@ test('correction follow-up is typed and duplicate confirmation remains readiness ); }); -test('relationship correction is closed, revisioned, interval checked, and has no caller authority hints', () => { +void test('relationship correction is closed, revisioned, interval checked, and has no caller authority hints', () => { const strictDecode = Schema.decodeUnknownSync(PartyCorrectionCommandSchema, { onExcessProperty: 'error', }); @@ -227,7 +227,7 @@ const transactionHarness = ( return { insertValues, transaction, updateSets }; }; -test('relationship supersession preserves endpoint/type identity and stores trusted actor plus old/new links', async () => { +void test('relationship supersession preserves endpoint/type identity and stores trusted actor plus old/new links', async () => { const original = relationshipRow(); const replacement = relationshipRow({ relationshipId: replacementId }); const h = transactionHarness( @@ -255,7 +255,7 @@ test('relationship supersession preserves endpoint/type identity and stores trus assert.equal(Option.getOrThrow(result.replacementRelationshipRef).resourceId, replacementId); }); -test('relationship retraction retains original effective validity and creates no replacement', async () => { +void test('relationship retraction retains original effective validity and creates no replacement', async () => { const command = decode(PartyCorrectionCommandSchema)({ ...evidence, correctionMode: 'RETRACT', @@ -277,7 +277,7 @@ test('relationship retraction retains original effective validity and creates no assert.ok(Option.isNone(result.replacementAssertionId)); }); -test('stale revision and foreign-tenant relationship correction fail before business writes', async () => { +void test('stale revision and foreign-tenant relationship correction fail before business writes', async () => { await Promise.all( [ decode(SupersedeRelationshipCorrectionCommandSchema)({ @@ -298,14 +298,14 @@ test('stale revision and foreign-tenant relationship correction fail before busi }), ), ); - assert.equal(error._tag, 'PartyCorrectionConflict'); + assert.ok(Predicate.isTagged(error, 'PartyCorrectionConflict')); assert.equal(h.updateSets.length, 0); assert.equal(h.insertValues.length, 0); }), ); }); -test('UNRESOLVED Party Type enrichment is rejected before mutation by correction', async () => { +void test('UNRESOLVED Party Type enrichment is rejected before mutation by correction', async () => { const h = transactionHarness([ [], [{ partyId }], @@ -344,12 +344,12 @@ test('UNRESOLVED Party Type enrichment is rejected before mutation by correction correctPartyFactRecord(h.transaction, tenantId, command, { actionInvocationId, principalId }), ), ); - assert.equal(error._tag, 'PartyCorrectionConflict'); + assert.ok(Predicate.isTagged(error, 'PartyCorrectionConflict')); assert.match(error.reason, /enrichment/u); assert.equal(h.updateSets.length, 0); }); -test('detail exposes immutable original/result semantics, governance, and source distinct from actor', async () => { +void test('detail exposes immutable original/result semantics, governance, and source distinct from actor', async () => { const h = transactionHarness([ [ { @@ -417,7 +417,7 @@ test('detail exposes immutable original/result semantics, governance, and source assert.equal(detail.governance.policyVersion, detail.policyVersion); }); -test('relationship overlap is a typed conflict and no correction journal is written after failed replacement', async () => { +void test('relationship overlap is a typed conflict and no correction journal is written after failed replacement', async () => { const original = relationshipRow(); const h = transactionHarness( [[], [original], [], [{ partyId }], [], [{ partyId: organizationId }]], @@ -433,14 +433,14 @@ test('relationship overlap is a typed conflict and no correction journal is writ }), ), ); - assert.equal(error._tag, 'PartyCorrectionConflict'); + assert.ok(Predicate.isTagged(error, 'PartyCorrectionConflict')); assert.match(error.reason, /overlaps/u); assert.equal(h.insertValues.length, 1); // The failed Effect leaves the enclosing Core transaction to roll back the original transition. assert.equal(h.insertValues[0]?.['supersedesRelationshipId'], assertionId); }); -test('correction of a durable relationship preserves stored alias endpoints', async () => { +void test('correction of a durable relationship preserves stored alias endpoints', async () => { const canonicalId = '90000000-0000-4000-8000-000000000001'; const original = relationshipRow(); const h = transactionHarness( @@ -466,13 +466,13 @@ test('correction of a durable relationship preserves stored alias endpoints', as assert.notEqual(h.insertValues[0]?.['fromPartyId'], canonicalId); }); -test('correction history requires reviewer authority; ordinary identity read permission is insufficient', () => { +void test('correction history requires reviewer authority; ordinary identity read permission is insufficient', () => { const target = partyCorrectionPermissionTarget(); assert.deepEqual(target, { kind: 'tenant', permission: 'review_party_identity' }); assert.notDeepEqual(target, { kind: 'tenant', permission: 'read_party_identity' }); }); -test('Party Type correction reconciles newly eligible claims before superseding the original fact', async () => { +void test('Party Type correction reconciles newly eligible claims before superseding the original fact', async () => { const h = transactionHarness([ [], [{ partyId }], @@ -523,13 +523,13 @@ test('Party Type correction reconciles newly eligible claims before superseding correctPartyFactRecord(h.transaction, tenantId, command, { actionInvocationId, principalId }), ), ); - assert.equal(error._tag, 'PartyCorrectionConflict'); + assert.ok(Predicate.isTagged(error, 'PartyCorrectionConflict')); assert.match(error.reason, /exclusive identifier claims/u); assert.equal(h.updateSets.length, 0); assert.equal(h.insertValues.length, 0); }); -test('type Correction cannot treat a reviewer decision or source label as subject evidence', async () => { +void test('type Correction cannot treat a reviewer decision or source label as subject evidence', async () => { const h = transactionHarness([ [], [{ partyId }], @@ -558,7 +558,7 @@ test('type Correction cannot treat a reviewer decision or source label as subjec correctPartyFactRecord(h.transaction, tenantId, command, { actionInvocationId, principalId }), ), ); - assert.equal(error._tag, 'PartyCorrectionConflict'); + assert.ok(Predicate.isTagged(error, 'PartyCorrectionConflict')); assert.equal(error.reason, 'subject_evidence_required'); assert.equal(h.insertValues.length, 0); assert.equal(h.updateSets.length, 0); diff --git a/app/verticals/party-registry/tests/unit/counterparty-persistence.service.test.ts b/app/verticals/party-registry/tests/unit/counterparty-persistence.service.test.ts index f81677f28..190023ac6 100644 --- a/app/verticals/party-registry/tests/unit/counterparty-persistence.service.test.ts +++ b/app/verticals/party-registry/tests/unit/counterparty-persistence.service.test.ts @@ -1,5 +1,5 @@ import { runEffectTestPromise } from '@app/core-runtime/testing/effect-runtime'; -import { DateTime, Effect } from 'effect'; +import { DateTime, Effect, Predicate } from 'effect'; /* eslint-disable anti-slop/no-chained-type-assertions, anti-slop/no-unsafe-dictionary-type -- This focused test harness models the narrow Drizzle native Effect query surface used by the owner-local service. expires: 2026-12-31. */ import type { Table } from 'drizzle-orm'; import { getTableName } from 'drizzle-orm'; @@ -146,7 +146,7 @@ const endInput = (validTo: string, method: string) => ({ validTo, }); -test('keeps a future-ended role active until its exclusive effective end', () => { +void test('keeps a future-ended role active until its exclusive effective end', () => { const futureEnd = '2099-01-01T00:00:00.000Z'; const updated = roleRow({ endEvidenceRefs: ['contract:end'], @@ -166,7 +166,7 @@ test('keeps a future-ended role active until its exclusive effective end', () => endInput(futureEnd, 'CONFIRMED_CUSTOMER_RELATIONSHIP_END'), ), ).then((result) => { - assert.equal(result._tag, 'found'); + assert.ok(Predicate.isTagged(result, 'found')); assert.equal(harness.updateSets[0]?.['state'], 'ACTIVE'); assert.equal(harness.updateSets[0]?.['isCurrent'], true); assert.equal(harness.updateSets[0]?.['endProvenanceSource'], 'contracts.core'); @@ -182,7 +182,7 @@ test('keeps a future-ended role active until its exclusive effective end', () => }); }); -test('records a retrospective end as historical without deleting the role period', () => { +void test('records a retrospective end as historical without deleting the role period', () => { const pastEnd = '2021-01-01T00:00:00.000Z'; const updated = roleRow({ endEvidenceRefs: ['contract:end'], @@ -205,7 +205,7 @@ test('records a retrospective end as historical without deleting the role period }); }); -test('rejects inactivity evidence before persisting a CUSTOMER end', () => { +void test('rejects inactivity evidence before persisting a CUSTOMER end', () => { const harness = transactionHarness([[counterpartyRow], [roleRow()]]); return runEffectTestPromise( @@ -223,7 +223,7 @@ test('rejects inactivity evidence before persisting a CUSTOMER end', () => { }); }); -test('reuses an exactly repeated end without another write', () => { +void test('reuses an exactly repeated end without another write', () => { const validTo = '2025-01-01T00:00:00.000Z'; const ended = roleRow({ endEvidenceRefs: ['contract:end'], @@ -241,13 +241,13 @@ test('reuses an exactly repeated end without another write', () => { endInput(validTo, 'CONFIRMED_CUSTOMER_RELATIONSHIP_END'), ), ).then((result) => { - assert.equal(result._tag, 'found'); + assert.ok(Predicate.isTagged(result, 'found')); assert.equal(result.changed, false); assert.equal(harness.updateSets.length, 0); }); }); -test('reads end provenance independently from the role-add provenance', () => { +void test('reads end provenance independently from the role-add provenance', () => { const ended = roleRow({ endEvidenceRefs: ['contract:end'], endProvenanceMethod: 'CONFIRMED_CUSTOMER_RELATIONSHIP_END', @@ -261,7 +261,7 @@ test('reads end provenance independently from the role-add provenance', () => { return runEffectTestPromise( listCounterpartyRoleHistory(harness.transaction, tenantId, legalEntityId, counterpartyId), ).then((result) => { - assert.equal(result._tag, 'found'); + assert.ok(Predicate.isTagged(result, 'found')); assert.deepEqual(result.value[0]?.endProvenance, { evidenceReference: 'contract:end', method: 'CONFIRMED_CUSTOMER_RELATIONSHIP_END', @@ -271,7 +271,7 @@ test('reads end provenance independently from the role-add provenance', () => { }); }); -test('allows the authorized tenant-admin path to read history without payload Legal Entity data', () => { +void test('allows the authorized tenant-admin path to read history without payload Legal Entity data', () => { const harness = transactionHarness([ [{ ...counterpartyRow, storedPartyId: partyId }], [roleRow()], @@ -280,7 +280,7 @@ test('allows the authorized tenant-admin path to read history without payload Le return runEffectTestPromise( listCounterpartyRoleHistory(harness.transaction, tenantId, undefined, counterpartyId), ).then((result) => { - assert.equal(result._tag, 'found'); + assert.ok(Predicate.isTagged(result, 'found')); assert.equal(result.value[0]?.roleType, 'CUSTOMER'); assert.deepEqual(harness.selectedTables, [ 'counterparty_admin_read_models', @@ -289,7 +289,7 @@ test('allows the authorized tenant-admin path to read history without payload Le }); }); -test('rejects an alias Party create target with canonical survivor guidance', () => { +void test('rejects an alias Party create target with canonical survivor guidance', () => { const survivorId = '40000000-0000-4000-8000-000000000002'; const harness = transactionHarness([ [{ aliasPartyId: partyId, canonicalPartyId: survivorId, tenantId }], @@ -313,13 +313,13 @@ test('rejects an alias Party create target with canonical survivor guidance', () tenantId, }), ).then((result) => { - assert.equal(result._tag, 'party_alias'); + assert.ok(Predicate.isTagged(result, 'party_alias')); assert.equal(result.canonicalPartyRef.resourceId, survivorId); assert.equal(harness.insertValues.length, 0); }); }); -test('admin detail follows a complete Party alias chain while retaining the stored reference', () => { +void test('admin detail follows a complete Party alias chain while retaining the stored reference', () => { const middleId = '40000000-0000-4000-8000-000000000002'; const survivorId = '40000000-0000-4000-8000-000000000003'; const harness = transactionHarness([ @@ -343,7 +343,7 @@ test('admin detail follows a complete Party alias chain while retaining the stor return runEffectTestPromise( findCounterpartyRecord(harness.transaction, tenantId, undefined, counterpartyId), ).then((result) => { - assert.equal(result._tag, 'found'); + assert.ok(Predicate.isTagged(result, 'found')); assert.equal(result.value.party.storedPartyRef.resourceId, partyId); assert.equal(result.value.party.canonicalPartyRef.resourceId, survivorId); assert.equal(result.value.legalEntityRef.resourceId, legalEntityId); @@ -352,7 +352,7 @@ test('admin detail follows a complete Party alias chain while retaining the stor }); }); -test('creates the tenant-admin snapshot atomically without creating an implicit role', () => { +void test('creates the tenant-admin snapshot atomically without creating an implicit role', () => { const party = { archivedAt: null, partyId, tenantId }; const harness = transactionHarness( [[], [{ partyId }], [], [{ partyId }], [party]], @@ -376,13 +376,13 @@ test('creates the tenant-admin snapshot atomically without creating an implicit tenantId, }), ).then((result) => { - assert.equal(result._tag, 'found'); + assert.ok(Predicate.isTagged(result, 'found')); assert.deepEqual(harness.insertedTables, ['counterparties', 'counterparty_admin_read_models']); assert.equal(harness.insertValues[1]?.['storedPartyId'], partyId); }); }); -test('adds a future role and its admin history projection in the same transaction seam', () => { +void test('adds a future role and its admin history projection in the same transaction seam', () => { const futureStart = '2099-01-01T00:00:00.000Z'; const futureRole = roleRow({ isCurrent: false, validFrom: date(futureStart) }); const harness = transactionHarness( @@ -409,7 +409,7 @@ test('adds a future role and its admin history projection in the same transactio validTo: null, }), ).then((result) => { - assert.equal(result._tag, 'found'); + assert.ok(Predicate.isTagged(result, 'found')); assert.equal(harness.insertValues[0]?.['isCurrent'], false); assert.deepEqual(harness.insertedTables, [ 'counterparty_role_periods', diff --git a/app/verticals/party-registry/tests/unit/database-client.test.ts b/app/verticals/party-registry/tests/unit/database-client.test.ts index 017ab6189..df0c2ffa8 100644 --- a/app/verticals/party-registry/tests/unit/database-client.test.ts +++ b/app/verticals/party-registry/tests/unit/database-client.test.ts @@ -2,10 +2,10 @@ import { runEffectTestPromise } from '@app/core-runtime/testing/effect-runtime'; // @effect-diagnostics asyncFunction:off -- Existing compatibility boundary; expires: 2026-12-31. import assert from 'node:assert/strict'; import test from 'node:test'; -import { Effect } from 'effect'; +import { Effect, Predicate } from 'effect'; import { acquirePoolResource, makePartyDatabase } from '../../src/db/client.ts'; -test('finalizes the Party Registry pool when its Effect scope closes', async () => { +void test('finalizes the Party Registry pool when its Effect scope closes', async () => { let finalized = false; await runEffectTestPromise( Effect.scoped( @@ -20,7 +20,7 @@ test('finalizes the Party Registry pool when its Effect scope closes', async () assert.equal(finalized, true); }); -test('keeps Party Registry pool acquisition failure in the typed error channel', async () => { +void test('keeps Party Registry pool acquisition failure in the typed error channel', async () => { const error = await runEffectTestPromise( Effect.flip( Effect.scoped( @@ -39,6 +39,6 @@ test('keeps Party Registry pool acquisition failure in the typed error channel', ), ), ); - assert.equal(error._tag, 'PartyDatabaseConnectionError'); + assert.ok(Predicate.isTagged(error, 'PartyDatabaseConnectionError')); assert.equal(error.reason, 'Unable to initialize the Party Registry PostgreSQL connection pool'); }); diff --git a/app/verticals/party-registry/tests/unit/engagement-profile-persistence-service.test.ts b/app/verticals/party-registry/tests/unit/engagement-profile-persistence-service.test.ts index 5a7d3c3a0..28160e670 100644 --- a/app/verticals/party-registry/tests/unit/engagement-profile-persistence-service.test.ts +++ b/app/verticals/party-registry/tests/unit/engagement-profile-persistence-service.test.ts @@ -1,7 +1,7 @@ import { runEffectTestPromise } from '@app/core-runtime/testing/effect-runtime'; // @effect-diagnostics asyncFunction:off -- Existing compatibility boundary; expires: 2026-12-31. /* eslint-disable anti-slop/no-chained-type-assertions -- Focused harness implements only the mutation insert's Drizzle seam. expires: 2026-12-31. */ -import { DateTime, Effect } from 'effect'; +import { DateTime, Effect, Predicate } from 'effect'; import assert from 'node:assert/strict'; import test from 'node:test'; import type { OrganizationEngagementProfileRecord } from '../../src/db/engagement-schema.ts'; @@ -45,7 +45,7 @@ const rejectingMutationTransaction = (failure: Failure) => }), }) as unknown as Parameters[0]; -test('reconstructs typed references from the owner-local persistence record', () => { +void test('reconstructs typed references from the owner-local persistence record', () => { const result = organizationEngagementProfileFromRecord(row); assert.deepEqual(result.partyRef, refs.partyRef); assert.deepEqual(result.counterpartyRef, refs.counterpartyRef); @@ -58,7 +58,7 @@ test('reconstructs typed references from the owner-local persistence record', () ); }); -test('fails closed when a caller-supplied ref crosses the trusted tenant', async () => { +void test('fails closed when a caller-supplied ref crosses the trusted tenant', async () => { const failure = await runEffectTestPromise( Effect.flip( ensureReferencesBelongToTenant(tenantId, { @@ -67,11 +67,11 @@ test('fails closed when a caller-supplied ref crosses the trusted tenant', async }), ), ); - assert.equal(failure._tag, 'EngagementProfileConflict'); + assert.ok(Predicate.isTagged(failure, 'EngagementProfileConflict')); assert.equal(failure.code, 'contacts_party_counterparty_mismatch'); }); -test('maps a wrapped owner uniqueness constraint to the declared engagement conflict', async () => { +void test('maps a wrapped owner uniqueness constraint to the declared engagement conflict', async () => { const failure = await runEffectTestPromise( Effect.flip( createOrganizationEngagementProfile( @@ -88,7 +88,7 @@ test('maps a wrapped owner uniqueness constraint to the declared engagement conf ), ); - assert.equal(failure._tag, 'EngagementProfileConflict'); + assert.ok(Predicate.isTagged(failure, 'EngagementProfileConflict')); assert.equal(failure.code, 'contacts_engagement_profile_already_exists'); assert.equal( failure.reason, @@ -96,7 +96,7 @@ test('maps a wrapped owner uniqueness constraint to the declared engagement conf ); }); -test('continues past an unrelated wrapper code to the owner uniqueness constraint', async () => { +void test('continues past an unrelated wrapper code to the owner uniqueness constraint', async () => { const failure = await runEffectTestPromise( Effect.flip( createOrganizationEngagementProfile( @@ -112,11 +112,11 @@ test('continues past an unrelated wrapper code to the owner uniqueness constrain ), ); - assert.equal(failure._tag, 'EngagementProfileConflict'); + assert.ok(Predicate.isTagged(failure, 'EngagementProfileConflict')); assert.equal(failure.code, 'contacts_engagement_profile_already_exists'); }); -test('maps an unrelated uniqueness constraint to the existing persistence fallback', async () => { +void test('maps an unrelated uniqueness constraint to the existing persistence fallback', async () => { const failure = await runEffectTestPromise( Effect.flip( createOrganizationEngagementProfile( @@ -129,7 +129,7 @@ test('maps an unrelated uniqueness constraint to the existing persistence fallba ), ); - assert.equal(failure._tag, 'EngagementProfilePersistenceUnavailable'); + assert.ok(Predicate.isTagged(failure, 'EngagementProfilePersistenceUnavailable')); assert.equal(failure.code, 'contacts_engagement_profile_persistence_unavailable'); assert.equal( failure.reason, diff --git a/app/verticals/party-registry/tests/unit/identifier-persistence.service.test.ts b/app/verticals/party-registry/tests/unit/identifier-persistence.service.test.ts index deadd944c..8d4a4d79d 100644 --- a/app/verticals/party-registry/tests/unit/identifier-persistence.service.test.ts +++ b/app/verticals/party-registry/tests/unit/identifier-persistence.service.test.ts @@ -1,5 +1,5 @@ import { runEffectTestPromise } from '@app/core-runtime/testing/effect-runtime'; -import { DateTime, Effect, Match, Schema } from 'effect'; +import { DateTime, Effect, Match, Schema, Predicate } from 'effect'; // @effect-diagnostics asyncFunction:off globalDate:off -- Existing compatibility boundary; expires: 2026-12-31. /* eslint-disable anti-slop/no-chained-type-assertions, anti-slop/no-unsafe-dictionary-type -- Focused harness implements only the owner service's Drizzle seam. expires: 2026-12-31. */ import type { SQL } from 'drizzle-orm'; @@ -148,7 +148,7 @@ const harness = ( return { deleted: () => deletes, inserts, lockedTables, transaction, updates }; }; -test('Add reuses a current same-Party identifier instead of duplicating an assertion', async () => { +void test('Add reuses a current same-Party identifier instead of duplicating an assertion', async () => { const db = harness(); const result = await runEffectTestPromise( addOfficialIdentifierRecord(db.transaction, tenantId, partyId, identifier, { @@ -165,7 +165,7 @@ test('Add reuses a current same-Party identifier instead of duplicating an asser assert.equal(db.inserts.length, 0); }); -test('Add retains ARES evidence separately from the accepting actor and only claims eligible Party types', async () => { +void test('Add retains ARES evidence separately from the accepting actor and only claims eligible Party types', async () => { const externalEvidenceWire = { authorityPolicyKey: 'party_registry.ares_enrichment', authorityPolicyVersion: '1', @@ -214,7 +214,7 @@ test('Add retains ARES evidence separately from the accepting actor and only cla ); }); -test('ending an identifier preserves its fact and releases its current claim', async () => { +void test('ending an identifier preserves its fact and releases its current claim', async () => { const db = harness({ claimOwner: partyId, current: row({ verificationState: 'VERIFIED', verifiedAt: date('2026-01-01T00:00:00.000Z') }), @@ -227,14 +227,14 @@ test('ending an identifier preserves its fact and releases its current claim', a '2026-02-01T00:00:00.000Z', ), ); - assert.equal(result._tag, 'found'); + assert.ok(Predicate.isTagged(result, 'found')); assert.equal(db.updates[0]?.['state'], 'ENDED'); assert.equal(db.updates[0]?.['isCurrent'], false); assert.equal(db.deleted(), 1); assert.deepEqual(db.lockedTables, [parties, partyOfficialIdentifiers]); }); -test('a future end does not release a presently valid claim', async () => { +void test('a future end does not release a presently valid claim', async () => { const db = harness(); const result = await runEffectTestPromise( endOfficialIdentifierRecord( @@ -244,7 +244,7 @@ test('a future end does not release a presently valid claim', async () => { '2099-01-01T00:00:00.000Z', ), ); - assert.equal(result._tag, 'conflict'); + assert.ok(Predicate.isTagged(result, 'conflict')); assert.equal(db.updates.length, 0); assert.equal(db.deleted(), 0); }); @@ -256,7 +256,7 @@ const verificationCommand = { verification: 'VERIFIED', } as const; -test('verification collision changes neither metadata nor claim ownership', async () => { +void test('verification collision changes neither metadata nor claim ownership', async () => { const db = harness({ claimOwner: 'another-party' }); const result = await runEffectTestPromise( updateOfficialIdentifierVerificationRecord( @@ -266,12 +266,12 @@ test('verification collision changes neither metadata nor claim ownership', asyn verificationCommand, ), ); - assert.equal(result._tag, 'claim_conflict'); + assert.ok(Predicate.isTagged(result, 'claim_conflict')); assert.equal(db.updates.length, 0); assert.equal(db.inserts.length, 0); }); -test('verification preserves before-state and immutable identity/provenance while acquiring an eligible claim', async () => { +void test('verification preserves before-state and immutable identity/provenance while acquiring an eligible claim', async () => { const db = harness(); const result = await runEffectTestPromise( updateOfficialIdentifierVerificationRecord( @@ -281,7 +281,7 @@ test('verification preserves before-state and immutable identity/provenance whil verificationCommand, ), ); - assert.equal(result._tag, 'found'); + assert.ok(Predicate.isTagged(result, 'found')); const found = Match.value(result).pipe( Match.tag('found', (value) => value), Match.orElse(() => assert.fail('Expected the identifier verification update to succeed')), @@ -297,7 +297,7 @@ test('verification preserves before-state and immutable identity/provenance whil assert.equal(db.inserts[0]?.table, partyIdentifierClaims); }); -test('PERSON verification cannot acquire an implicit strong identifier claim', async () => { +void test('PERSON verification cannot acquire an implicit strong identifier claim', async () => { const db = harness({ partyType: 'PERSON' }); const result = await runEffectTestPromise( updateOfficialIdentifierVerificationRecord( @@ -307,11 +307,11 @@ test('PERSON verification cannot acquire an implicit strong identifier claim', a verificationCommand, ), ); - assert.equal(result._tag, 'found'); + assert.ok(Predicate.isTagged(result, 'found')); assert.equal(db.inserts.length, 0); }); -test('verification downgrade releases its claim without erasing the previous verification evidence', async () => { +void test('verification downgrade releases its claim without erasing the previous verification evidence', async () => { const verifiedAt = date('2026-01-01T00:00:00.000Z'); const db = harness({ claimOwner: partyId, @@ -324,7 +324,7 @@ test('verification downgrade releases its claim without erasing the previous ver verification: 'REJECTED', }), ); - assert.equal(result._tag, 'found'); + assert.ok(Predicate.isTagged(result, 'found')); const found = Match.value(result).pipe( Match.tag('found', (value) => value), Match.orElse(() => assert.fail('Expected the identifier verification downgrade to succeed')), @@ -335,7 +335,7 @@ test('verification downgrade releases its claim without erasing the previous ver assert.equal(db.deleted(), 1); }); -test('archived Party and stale verification updates are rejected before mutation', async () => { +void test('archived Party and stale verification updates are rejected before mutation', async () => { await Promise.all( [harness({ archived: true }), harness({ current: row({ verificationState: 'REJECTED' }) })].map( async (db) => { @@ -347,7 +347,7 @@ test('archived Party and stale verification updates are rejected before mutation verificationCommand, ), ); - assert.equal(result._tag, 'conflict'); + assert.ok(Predicate.isTagged(result, 'conflict')); assert.equal(db.updates.length, 0); }, ), diff --git a/app/verticals/party-registry/tests/unit/identity-action-evidence.test.ts b/app/verticals/party-registry/tests/unit/identity-action-evidence.test.ts index bc87df5b4..674856377 100644 --- a/app/verticals/party-registry/tests/unit/identity-action-evidence.test.ts +++ b/app/verticals/party-registry/tests/unit/identity-action-evidence.test.ts @@ -1,7 +1,7 @@ import { runEffectTestPromise } from '@app/core-runtime/testing/effect-runtime'; import assert from 'node:assert/strict'; import test from 'node:test'; -import { DateTime, Effect, Option, Schema } from 'effect'; +import { DateTime, Effect, Option, Schema, Predicate } from 'effect'; import { bindActionTestServices, makeActionTestHarness } from '@app/core-runtime/testing/actions'; import { createActionCollector } from '../../../../packages/core-runtime/src/actions/collector.ts'; import { getActionHandler } from '../../../../packages/core-runtime/src/actions/definition.ts'; @@ -44,7 +44,7 @@ const assertInvariantEvidence = (snapshot: ActionEvidenceSnapshot) => { assert.equal(snapshot.outboxMessages.length, 1); }; -test('Update Party records metadata-only invariant evidence with its event and outbox', () => +void test('Update Party records metadata-only invariant evidence with its event and outbox', () => runEffectTestPromise( Effect.gen(function* verifyUpdateEvidence() { const collector = createActionCollector( @@ -72,7 +72,7 @@ test('Update Party records metadata-only invariant evidence with its event and o }), )); -test('Archive Party records metadata-only invariant evidence with its event and outbox', () => +void test('Archive Party records metadata-only invariant evidence with its event and outbox', () => runEffectTestPromise( Effect.gen(function* verifyArchiveEvidence() { const collector = createActionCollector( @@ -93,7 +93,7 @@ test('Archive Party records metadata-only invariant evidence with its event and }), )); -test('Unarchive Party records metadata-only invariant evidence with its event and outbox', () => +void test('Unarchive Party records metadata-only invariant evidence with its event and outbox', () => runEffectTestPromise( Effect.gen(function* verifyUnarchiveEvidence() { const collector = createActionCollector( @@ -114,7 +114,7 @@ test('Unarchive Party records metadata-only invariant evidence with its event an }), )); -test('Unarchive review outcome commits once and replays without an unarchive event or outbox', () => +void test('Unarchive review outcome commits once and replays without an unarchive event or outbox', () => runEffectTestPromise( Effect.gen(function* verifyUnarchiveConflictEvidence() { let calls = 0; @@ -155,7 +155,7 @@ test('Unarchive review outcome commits once and replays without an unarchive eve }; assert.deepEqual(yield* harness.runtime.runAction(request), blocked); const replay = yield* harness.runtime.runAction(request).pipe(Effect.flip); - assert.equal(replay._tag, 'ActionAlreadyCommitted'); + assert.ok(Predicate.isTagged(replay, 'ActionAlreadyCommitted')); assert.equal(calls, 1); const snapshot = harness.snapshot(); assert.equal(snapshot.committed.length, 1); diff --git a/app/verticals/party-registry/tests/unit/identity-party-detail-alias.test.ts b/app/verticals/party-registry/tests/unit/identity-party-detail-alias.test.ts index 9d17133cc..378f732c9 100644 --- a/app/verticals/party-registry/tests/unit/identity-party-detail-alias.test.ts +++ b/app/verticals/party-registry/tests/unit/identity-party-detail-alias.test.ts @@ -1,7 +1,7 @@ import { runEffectTestSync } from '@app/core-runtime/testing/effect-runtime'; import assert from 'node:assert/strict'; import test from 'node:test'; -import { DateTime, Effect, Option, Schema } from 'effect'; +import { DateTime, Effect, Option, Schema, Predicate } from 'effect'; import { PartyDetailResponseSchema } from '../../shared/apis/party-detail.ts'; import { PartySchema } from '../../shared/domain/identity-contracts.ts'; import type { Party } from '../../shared/domain/identity-contracts.ts'; @@ -63,7 +63,7 @@ const makeServices = ( }; }; -test('Party Detail reads the final canonical Party after the complete historical alias chain', () => { +void test('Party Detail reads the final canonical Party after the complete historical alias chain', () => { const { lookups, services } = makeServices([ alias('party-b', 'party-a'), alias('party-a', 'party-c'), @@ -90,7 +90,7 @@ test('Party Detail reads the final canonical Party after the complete historical assert.deepEqual(encoded.party, canonicalPartyWire); }); -test('Party Detail preserves archived lifecycle independently of direct resolution metadata', () => { +void test('Party Detail preserves archived lifecycle independently of direct resolution metadata', () => { const archivedAt = '2026-09-02T10:00:00.000Z'; const archivedParty = Schema.decodeUnknownSync(PartySchema)({ ...canonicalPartyWire, @@ -110,7 +110,7 @@ test('Party Detail preserves archived lifecycle independently of direct resoluti }); }); -test('Party Detail fails closed for cycles and broken historical chains without reading an alias Party', () => { +void test('Party Detail fails closed for cycles and broken historical chains without reading an alias Party', () => { for (const aliases of [ [alias('party-a', 'party-b'), alias('party-b', 'party-a')], [alias('party-a', 'missing')], @@ -119,12 +119,12 @@ test('Party Detail fails closed for cycles and broken historical chains without const error = runEffectTestSync( Effect.flip(readPartyDetailFromServices(partyRef('party-a'), tenantId, services)), ); - assert.equal(error._tag, 'ReadHandlerUnavailable'); + assert.ok(Predicate.isTagged(error, 'ReadHandlerUnavailable')); assert.deepEqual(lookups, []); } }); -test('Party Detail hides a missing direct Party and a cross-tenant requested reference', () => { +void test('Party Detail hides a missing direct Party and a cross-tenant requested reference', () => { const { lookups, services } = makeServices([]); for (const requested of [ partyRef('missing'), @@ -133,7 +133,7 @@ test('Party Detail hides a missing direct Party and a cross-tenant requested ref const error = runEffectTestSync( Effect.flip(readPartyDetailFromServices(requested, tenantId, services)), ); - assert.equal(error._tag, 'ReadHandlerNotFound'); + assert.ok(Predicate.isTagged(error, 'ReadHandlerNotFound')); } assert.deepEqual(lookups, []); }); diff --git a/app/verticals/party-registry/tests/unit/identity-persistence.service.test.ts b/app/verticals/party-registry/tests/unit/identity-persistence.service.test.ts index f6ba39732..0bb8dfd71 100644 --- a/app/verticals/party-registry/tests/unit/identity-persistence.service.test.ts +++ b/app/verticals/party-registry/tests/unit/identity-persistence.service.test.ts @@ -1,7 +1,7 @@ import { runEffectTestPromise } from '@app/core-runtime/testing/effect-runtime'; import type { SQL } from 'drizzle-orm'; import { PgDialect } from 'drizzle-orm/pg-core'; -import { DateTime, Effect, Match, Option, Result, Schema } from 'effect'; +import { DateTime, Effect, Match, Option, Result, Schema, Predicate } from 'effect'; import assert from 'node:assert/strict'; import test from 'node:test'; import type { AresAppliedEvidence } from '../../shared/domain/ares-application.ts'; @@ -150,11 +150,11 @@ const assertTenantLockIsFirst = (harness: ReturnType) assert.deepEqual(query.params, [tenantIdentityWriteLockKey(tenantId)]); }; -test('ended Party facts are made non-current as part of the same transition', () => { +void test('ended Party facts are made non-current as part of the same transition', () => { assert.deepEqual(endedPartyFactTransition, { isCurrent: false, state: 'ENDED' }); }); -test('unnamed Party insertion persists no fabricated display-name assertion', () => +void test('unnamed Party insertion persists no fabricated display-name assertion', () => runEffectTestPromise( Effect.gen(function* verifyIdentityPersistence() { const candidateEvidence: AresAppliedEvidence = { @@ -218,7 +218,7 @@ test('unnamed Party insertion persists no fabricated display-name assertion', () }), )); -test('identity updates close the preceding assertion before accepting its replacement', () => +void test('identity updates close the preceding assertion before accepting its replacement', () => runEffectTestPromise( Effect.gen(function* verifyIdentityPersistence() { const current = partyRow({ archivedAt: null }); @@ -240,7 +240,7 @@ test('identity updates close the preceding assertion before accepting its replac provenanceSource: 'test', validFrom: '2026-01-01T00:00:00.000Z', }); - assert.equal(result._tag, 'found'); + assert.ok(Predicate.isTagged(result, 'found')); assertTenantLockIsFirst(harness); assert.deepEqual(harness.updateSets[1], { isCurrent: false, @@ -255,7 +255,7 @@ test('identity updates close the preceding assertion before accepting its replac }), )); -test('unarchive owner classification distinguishes conflict from ambiguity deterministically', () => { +void test('unarchive owner classification distinguishes conflict from ambiguity deterministically', () => { assert.deepEqual(classifyUnarchiveClaimOwners(partyId, [{}, { partyId }]), { _tag: 'available', }); @@ -276,7 +276,7 @@ test('unarchive owner classification distinguishes conflict from ambiguity deter ); }); -test('future-effective identity updates do not replace current facts early', () => +void test('future-effective identity updates do not replace current facts early', () => runEffectTestPromise( Effect.gen(function* verifyIdentityPersistence() { const harness = transactionHarness([[partyRow({ archivedAt: null })], [], [{ partyId }]]); @@ -290,13 +290,13 @@ test('future-effective identity updates do not replace current facts early', () provenanceSource: 'test', validFrom: '2999-01-01T00:00:00.000Z', }); - assert.equal(result._tag, 'conflict'); + assert.ok(Predicate.isTagged(result, 'conflict')); assert.deepEqual(harness.insertedValues, []); assert.deepEqual(harness.updateSets, []); }), )); -test('unarchive keeps the Party archived when an exact claim belongs to another Party', () => +void test('unarchive keeps the Party archived when an exact claim belongs to another Party', () => runEffectTestPromise( Effect.gen(function* verifyIdentityPersistence() { const harness = transactionHarness([ @@ -321,7 +321,7 @@ test('unarchive keeps the Party archived when an exact claim belongs to another }), )); -test('blocked unarchive persists a case and decision without mutating Party, then reuses the case on a fresh attempt', () => +void test('blocked unarchive persists a case and decision without mutating Party, then reuses the case on a fresh attempt', () => runEffectTestPromise( Effect.gen(function* verifyDurableUnarchiveReview() { const candidateCaseId = '66666666-6666-4666-8666-666666666666'; @@ -362,7 +362,7 @@ test('blocked unarchive persists a case and decision without mutating Party, the 4, decisionId, ); - assert.equal(result._tag, 'blocked'); + assert.ok(Predicate.isTagged(result, 'blocked')); const blocked = Match.value(result).pipe( Match.tag('blocked', ({ value }) => value), Match.orElse(() => assert.fail('Expected unarchive to be blocked')), @@ -414,7 +414,7 @@ test('blocked unarchive persists a case and decision without mutating Party, the 4, secondDecisionId, ); - assert.equal(retry._tag, 'blocked'); + assert.ok(Predicate.isTagged(retry, 'blocked')); const retryBlocked = Match.value(retry).pipe( Match.tag('blocked', ({ value }) => value), Match.orElse(() => assert.fail('Expected retry to be blocked')), @@ -427,7 +427,7 @@ test('blocked unarchive persists a case and decision without mutating Party, the }), )); -test('unresolved unnamed unarchive review persists no invented display-name evidence', () => +void test('unresolved unnamed unarchive review persists no invented display-name evidence', () => runEffectTestPromise( Effect.gen(function* verifyUnresolvedUnarchiveReview() { const current = partyRow({ currentDisplayName: null, currentType: 'UNRESOLVED' }); @@ -449,7 +449,7 @@ test('unresolved unnamed unarchive review persists no invented display-name evid 4, secondOwnerId, ); - assert.equal(result._tag, 'blocked'); + assert.ok(Predicate.isTagged(result, 'blocked')); const blocked = Match.value(result).pipe( Match.tag('blocked', ({ value }) => value), Match.orElse(() => assert.fail('Expected unarchive to be blocked')), @@ -464,7 +464,7 @@ test('unresolved unnamed unarchive review persists no invented display-name evid }), )); -test('archive acquires the tenant identity lock before any Party row lock', () => +void test('archive acquires the tenant identity lock before any Party row lock', () => runEffectTestPromise( Effect.gen(function* verifyIdentityPersistence() { const harness = transactionHarness([[]]); @@ -475,12 +475,12 @@ test('archive acquires the tenant identity lock before any Party row lock', () = 4, 'ARCHIVED', ); - assert.equal(result._tag, 'not_found'); + assert.ok(Predicate.isTagged(result, 'not_found')); assertTenantLockIsFirst(harness); }), )); -test('unarchive restores an unclaimed eligible identifier before activating the Party', () => +void test('unarchive restores an unclaimed eligible identifier before activating the Party', () => runEffectTestPromise( Effect.gen(function* verifyIdentityPersistence() { const activeParty = partyRow({ archivedAt: null, revision: 5 }); @@ -491,7 +491,7 @@ test('unarchive restores an unclaimed eligible identifier before activating the const result = yield* unarchivePartyRecord(harness.transaction, tenantId, partyId, 4); - assert.equal(result._tag, 'found'); + assert.ok(Predicate.isTagged(result, 'found')); assert.deepEqual(harness.insertedValues, [ [ { @@ -517,7 +517,7 @@ test('unarchive restores an unclaimed eligible identifier before activating the }), )); -test('unarchive rejects an alias rather than forwarding the write to its survivor', () => +void test('unarchive rejects an alias rather than forwarding the write to its survivor', () => runEffectTestPromise( Effect.gen(function* verifyIdentityPersistence() { const harness = transactionHarness([ @@ -530,13 +530,13 @@ test('unarchive rejects an alias rather than forwarding the write to its survivo const error = yield* Effect.flip( unarchivePartyRecord(harness.transaction, tenantId, partyId, 4), ); - assert.equal(error._tag, 'PartyAliasWriteRejected'); + assert.ok(Predicate.isTagged(error, 'PartyAliasWriteRejected')); assert.deepEqual(harness.insertedValues, []); assert.deepEqual(harness.updateSets, []); }), )); -test('unarchive reports ambiguous exact claims without changing archived state', () => +void test('unarchive reports ambiguous exact claims without changing archived state', () => runEffectTestPromise( Effect.gen(function* verifyIdentityPersistence() { const harness = transactionHarness([ @@ -568,7 +568,7 @@ test('unarchive reports ambiguous exact claims without changing archived state', }), )); -test('unarchive does not promote a PERSON ICO into an exclusive strong claim', () => +void test('unarchive does not promote a PERSON ICO into an exclusive strong claim', () => runEffectTestPromise( Effect.gen(function* verifyIdentityPersistence() { const currentParty = partyRow({ currentType: 'PERSON' }); @@ -578,13 +578,13 @@ test('unarchive does not promote a PERSON ICO into an exclusive strong claim', ( ); const result = yield* unarchivePartyRecord(harness.transaction, tenantId, partyId, 4); - assert.equal(result._tag, 'found'); + assert.ok(Predicate.isTagged(result, 'found')); assert.deepEqual(harness.insertedValues, []); assert.equal(harness.updateSets.length, 1); }), )); -test('unarchive requires review while a duplicate case involving the Party remains open', () => +void test('unarchive requires review while a duplicate case involving the Party remains open', () => runEffectTestPromise( Effect.gen(function* verifyIdentityPersistence() { const caseId = '88888888-8888-4888-8888-888888888888'; @@ -605,7 +605,7 @@ test('unarchive requires review while a duplicate case involving the Party remai }), )); -test('unarchive requires review for unresolved identity without any eligible strong claim', () => +void test('unarchive requires review for unresolved identity without any eligible strong claim', () => runEffectTestPromise( Effect.gen(function* verifyIdentityPersistence() { const harness = transactionHarness([ @@ -626,7 +626,7 @@ test('unarchive requires review for unresolved identity without any eligible str }), )); -test('reviewed UNRESOLVED Party can unarchive using retained accepted creation evidence', () => +void test('reviewed UNRESOLVED Party can unarchive using retained accepted creation evidence', () => runEffectTestPromise( Effect.gen(function* restoreReviewedUnresolved() { const current = partyRow({ currentType: 'UNRESOLVED' }); @@ -643,13 +643,13 @@ test('reviewed UNRESOLVED Party can unarchive using retained accepted creation e [[{ ...current, archivedAt: null, revision: 5 }]], ); const result = yield* unarchivePartyRecord(harness.transaction, tenantId, partyId, 4); - assert.equal(result._tag, 'found'); + assert.ok(Predicate.isTagged(result, 'found')); assert.equal(harness.updateSets.length, 1); assert.deepEqual(harness.insertedValues, []); }), )); -test('Party type enrichment refuses another owner of a newly eligible identifier', () => +void test('Party type enrichment refuses another owner of a newly eligible identifier', () => runEffectTestPromise( Effect.gen(function* preventEnrichmentCollision() { const current = partyRow({ archivedAt: null, currentType: 'UNRESOLVED' }); @@ -682,14 +682,14 @@ test('Party type enrichment refuses another owner of a newly eligible identifier ], validFrom: '2026-01-01T00:00:00.000Z', }); - assert.equal(result._tag, 'conflict'); + assert.ok(Predicate.isTagged(result, 'conflict')); assert.deepEqual(harness.insertedValues, []); assert.deepEqual(harness.updateSets, []); assert.deepEqual(harness.deletedTargets, []); }), )); -test('Party type enrichment atomically claims identifiers that newly qualify', () => +void test('Party type enrichment atomically claims identifiers that newly qualify', () => runEffectTestPromise( Effect.gen(function* claimEnrichedIdentifier() { const current = partyRow({ archivedAt: null, currentType: 'UNRESOLVED' }); @@ -717,7 +717,7 @@ test('Party type enrichment atomically claims identifiers that newly qualify', ( ], validFrom: '2026-01-01T00:00:00.000Z', }); - assert.equal(result._tag, 'found'); + assert.ok(Predicate.isTagged(result, 'found')); assert.deepEqual(harness.insertedValues[0], [ { identifierTypeKey: 'ICO', @@ -732,7 +732,7 @@ test('Party type enrichment atomically claims identifiers that newly qualify', ( }), )); -test('type correction reconciliation releases an ICO claim no longer eligible for a PERSON', () => +void test('type correction reconciliation releases an ICO claim no longer eligible for a PERSON', () => runEffectTestPromise( Effect.gen(function* releaseIneligibleClaim() { const harness = transactionHarness([ @@ -762,7 +762,7 @@ test('type correction reconciliation releases an ICO claim no longer eligible fo }), )); -test('identity updates reject a historical end earlier than the assertion being replaced', () => +void test('identity updates reject a historical end earlier than the assertion being replaced', () => runEffectTestPromise( Effect.gen(function* rejectInvalidHistoricalInterval() { const harness = transactionHarness([ @@ -781,14 +781,14 @@ test('identity updates reject a historical end earlier than the assertion being provenanceSource: 'test', validFrom: '2026-01-01T00:00:00.000Z', }); - assert.equal(result._tag, 'conflict'); + assert.ok(Predicate.isTagged(result, 'conflict')); assert.deepEqual(harness.insertedValues, []); assert.deepEqual(harness.updateSets, []); assert.deepEqual(harness.deletedTargets, []); }), )); -test('type enrichment rejects unevidenced type before accepting facts or claims', () => +void test('type enrichment rejects unevidenced type before accepting facts or claims', () => runEffectTestPromise( Effect.gen(function* rejectUnsupportedType() { const current = partyRow({ archivedAt: null, currentType: 'UNRESOLVED' }); @@ -805,7 +805,7 @@ test('type enrichment rejects unevidenced type before accepting facts or claims' validFrom: '2026-01-01T00:00:00.000Z', }), ); - assert.equal(error._tag, 'PartyEvidenceInsufficient'); + assert.ok(Predicate.isTagged(error, 'PartyEvidenceInsufficient')); assert.deepEqual(harness.insertedValues, []); assert.deepEqual(harness.updateSets, []); }), diff --git a/app/verticals/party-registry/tests/unit/matching-persistence.test.ts b/app/verticals/party-registry/tests/unit/matching-persistence.test.ts index 71191029d..5219cdeb3 100644 --- a/app/verticals/party-registry/tests/unit/matching-persistence.test.ts +++ b/app/verticals/party-registry/tests/unit/matching-persistence.test.ts @@ -1,7 +1,7 @@ import { runEffectTestPromise } from '@app/core-runtime/testing/effect-runtime'; /* eslint-disable anti-slop/no-chained-type-assertions, anti-slop/no-unsafe-dictionary-type -- This harness implements the narrow Drizzle Effect boundary exercised by the owner-local matching service. expires: 2026-12-31. */ import type { SQL } from 'drizzle-orm'; -import { DateTime, Effect, Schema } from 'effect'; +import { DateTime, Effect, Schema, Predicate } from 'effect'; import assert from 'node:assert/strict'; import test from 'node:test'; import { createActionCollector } from '../../../../packages/core-runtime/src/actions/collector.ts'; @@ -196,7 +196,7 @@ const ambiguousHarness = (existingCase: boolean) => ]), ); -test('durable Party Match commits an ambiguity decision, complete case references, and original evidence without mutating a Party', () => +void test('durable Party Match commits an ambiguity decision, complete case references, and original evidence without mutating a Party', () => runEffectTestPromise( Effect.gen(function* durablePartyMatchCommitsAnAmbiguityDecision() { const subject = ambiguousHarness(false); @@ -239,7 +239,7 @@ test('durable Party Match commits an ambiguity decision, complete case reference }), )); -test('an unchanged open ambiguity reuses its immutable evaluated case without rewriting candidate links', () => +void test('an unchanged open ambiguity reuses its immutable evaluated case without rewriting candidate links', () => runEffectTestPromise( Effect.gen(function* anUnchangedOpenAmbiguityReusesItsImmutable() { const subject = ambiguousHarness(true); @@ -256,7 +256,7 @@ test('an unchanged open ambiguity reuses its immutable evaluated case without re }), )); -test('PERSON IČO cannot acquire organization auto-match authority and NO_MATCH still records a decision', () => +void test('PERSON IČO cannot acquire organization auto-match authority and NO_MATCH still records a decision', () => runEffectTestPromise( Effect.gen(function* personIOCannotAcquireOrganizationAuto() { const subject = harness(); @@ -309,7 +309,7 @@ const resolutionInput = { tenantId, }; -test('an unarchive review cannot create a replacement Party or attach its facts through Candidate matching', () => +void test('an unarchive review cannot create a replacement Party or attach its facts through Candidate matching', () => runEffectTestPromise( Effect.gen(function* unarchiveIntentBoundary() { for (const resolution of ['CREATE', 'MATCH']) { @@ -337,7 +337,7 @@ test('an unarchive review cannot create a replacement Party or attach its facts : yield* Effect.flip( resolveDuplicateCandidateMatch(subject.transaction, resolutionInput), ); - assert.equal(error._tag, 'DuplicateCandidateConflict'); + assert.ok(Predicate.isTagged(error, 'DuplicateCandidateConflict')); assert.match(error.reason, /unarchive/iu); assert.deepEqual(subject.inserts, []); assert.deepEqual(subject.updates, []); @@ -362,7 +362,7 @@ const actionScope = { tenantId, }; -test('Create Party matching an existing subject publishes each newly accepted identifier without fabricating Party Created', () => +void test('Create Party matching an existing subject publishes each newly accepted identifier without fabricating Party Created', () => runEffectTestPromise( Effect.gen(function* createPartyMatchingAnExistingSubjectPublishes() { const subject = harness( @@ -424,7 +424,7 @@ test('Create Party matching an existing subject publishes each newly accepted id }), )); -test('reviewed matching publishes the accepted identifier through its declared Action event and linked outbox', () => +void test('reviewed matching publishes the accepted identifier through its declared Action event and linked outbox', () => runEffectTestPromise( Effect.gen(function* reviewedMatchingPublishesTheAcceptedIdentifierThrough() { const subject = harness( @@ -477,7 +477,7 @@ test('reviewed matching publishes the accepted identifier through its declared A }), )); -test('matched Create reusing an existing identifier does not republish an acceptance event', () => +void test('matched Create reusing an existing identifier does not republish an acceptance event', () => runEffectTestPromise( Effect.gen(function* matchedCreateReusingAnExistingIdentifierDoes() { const subject = harness( @@ -537,7 +537,7 @@ test('matched Create reusing an existing identifier does not republish an accept }), )); -test('repeated Candidate facts accepted in one matching transaction publish one identifier event', () => +void test('repeated Candidate facts accepted in one matching transaction publish one identifier event', () => runEffectTestPromise( Effect.gen(function* repeatedCandidateFactsAcceptedInOneMatching() { const subject = harness( @@ -603,7 +603,7 @@ test('repeated Candidate facts accepted in one matching transaction publish one }), )); -test('reviewed matching with already-owned claims creates no duplicate identifier notifications', () => +void test('reviewed matching with already-owned claims creates no duplicate identifier notifications', () => runEffectTestPromise( Effect.gen(function* reviewedMatchingWithAlreadyOwnedClaimsCreates() { const subject = harness( @@ -641,7 +641,7 @@ test('reviewed matching with already-owned claims creates no duplicate identifie }), )); -test('reviewed matching locks and rejects an archived canonical target before any attachment or resolution', () => +void test('reviewed matching locks and rejects an archived canonical target before any attachment or resolution', () => runEffectTestPromise( Effect.gen(function* reviewedMatchingLocksAndRejectsAnArchived() { const subject = harness( @@ -666,7 +666,7 @@ test('reviewed matching locks and rejects an archived canonical target before an const failure = yield* Effect.flip( resolveDuplicateCandidateMatch(subject.transaction, resolutionInput), ); - assert.equal(failure._tag, 'DuplicateCandidateConflict'); + assert.ok(Predicate.isTagged(failure, 'DuplicateCandidateConflict')); assert.equal( subject.reads.some(({ table, locked }) => table === parties && locked), true, @@ -676,7 +676,7 @@ test('reviewed matching locks and rejects an archived canonical target before an }), )); -test('reviewed matching rejects a cross-tenant selected reference without resolving its identity', () => +void test('reviewed matching rejects a cross-tenant selected reference without resolving its identity', () => runEffectTestPromise( Effect.gen(function* reviewedMatchingRejectsACrossTenantSelected() { const subject = harness(); @@ -686,7 +686,7 @@ test('reviewed matching rejects a cross-tenant selected reference without resolv selectedPartyTenantId: '90000000-0000-4000-8000-000000000001', }), ); - assert.equal(failure._tag, 'DuplicateCandidateConflict'); + assert.ok(Predicate.isTagged(failure, 'DuplicateCandidateConflict')); assert.equal( subject.reads.length, 1, @@ -696,7 +696,7 @@ test('reviewed matching rejects a cross-tenant selected reference without resolv }), )); -test('reviewed matching rejects an absorbed target with the full-chain canonical survivor reference', () => +void test('reviewed matching rejects an absorbed target with the full-chain canonical survivor reference', () => runEffectTestPromise( Effect.gen(function* reviewedMatchingRejectsAnAbsorbedTargetWith() { const subject = harness( @@ -726,7 +726,7 @@ test('reviewed matching rejects an absorbed target with the full-chain canonical }), )); -test('future-effective evidence is rejected before a current decision or Party can be persisted', () => +void test('future-effective evidence is rejected before a current decision or Party can be persisted', () => runEffectTestPromise( Effect.gen(function* futureEffectiveEvidenceIsRejectedBeforeA() { const subject = harness(); @@ -737,12 +737,12 @@ test('future-effective evidence is rejected before a current decision or Party c tenantId, }), ); - assert.equal(failure._tag, 'PartyEvidenceInsufficient'); + assert.ok(Predicate.isTagged(failure, 'PartyEvidenceInsufficient')); assert.deepEqual(subject.inserts, []); }), )); -test('durable matching is an idempotent identity Action and the separate UX preview remains a governed read', () => { +void test('durable matching is an idempotent identity Action and the separate UX preview remains a governed read', () => { assert.equal(matchPartyAction.descriptor.idempotency, 'required'); assert.equal( matchPartyAction.descriptor.tenantPermission?.({ candidate: candidate() }), @@ -752,7 +752,7 @@ test('durable matching is an idempotent identity Action and the separate UX prev assert.equal(partyMatchRead.descriptor.accessKind, 'detail'); }); -test('weak exact canonical evidence produces review rather than automatic identity or NO_MATCH', () => +void test('weak exact canonical evidence produces review rather than automatic identity or NO_MATCH', () => runEffectTestPromise( Effect.gen(function* weakExactCanonicalEvidenceProducesReviewRather() { const subject = harness( @@ -795,7 +795,7 @@ test('weak exact canonical evidence produces review rather than automatic identi }), )); -test('initial no-strong Create review captures relevant same-name canonical Parties in its immutable snapshot', () => +void test('initial no-strong Create review captures relevant same-name canonical Parties in its immutable snapshot', () => runEffectTestPromise( Effect.gen(function* initialNoStrongCreateReviewCapturesRelevant() { const subject = harness( @@ -830,7 +830,7 @@ test('initial no-strong Create review captures relevant same-name canonical Part }), )); -test('a new material evaluation creates a linked successor without rewriting the prior case', () => +void test('a new material evaluation creates a linked successor without rewriting the prior case', () => runEffectTestPromise( Effect.gen(function* aNewMaterialEvaluationCreatesALinked() { const priorId = '30000000-0000-4000-8000-000000000099'; @@ -871,7 +871,7 @@ test('a new material evaluation creates a linked successor without rewriting the }), )); -test('explicit prior-case continuation rejects foreign or missing review references', () => +void test('explicit prior-case continuation rejects foreign or missing review references', () => runEffectTestPromise( Effect.forEach( [tenantId, '90000000-0000-4000-8000-000000000001'], @@ -887,14 +887,14 @@ test('explicit prior-case continuation rejects foreign or missing review referen tenantId, }), ); - assert.equal(failure._tag, 'PartyEvidenceInsufficient'); + assert.ok(Predicate.isTagged(failure, 'PartyEvidenceInsufficient')); assert.deepEqual(subject.inserts, []); }), { concurrency: 'unbounded', discard: true }, ), )); -test('equivalent Candidate property and evidence ordering has one deterministic fingerprint', () => { +void test('equivalent Candidate property and evidence ordering has one deterministic fingerprint', () => { const original = candidate({ displayName: 'Northwind', evidenceRefs: ['evidence:b', 'evidence:a'], @@ -911,7 +911,7 @@ test('equivalent Candidate property and evidence ordering has one deterministic assert.equal(candidateFingerprint(original), candidateFingerprint(reordered)); }); -test('insufficient typed evidence cannot persist a case or decision even with a verified identifier', () => +void test('insufficient typed evidence cannot persist a case or decision even with a verified identifier', () => runEffectTestPromise( Effect.gen(function* denyUnevidencedSubject() { for (const operation of ['CREATE', 'MATCH'] as const) { @@ -925,13 +925,13 @@ test('insufficient typed evidence cannot persist a case or decision even with a const failure = yield* operation === 'CREATE' ? Effect.flip(createOrMatchParty(subject.transaction, input)) : Effect.flip(matchParty(subject.transaction, input)); - assert.equal(failure._tag, 'PartyEvidenceInsufficient'); + assert.ok(Predicate.isTagged(failure, 'PartyEvidenceInsufficient')); assert.equal(subject.inserts.length, 0); } }), )); -test('reviewer selection cannot waive missing subject/type evidence from a retained case', () => +void test('reviewer selection cannot waive missing subject/type evidence from a retained case', () => runEffectTestPromise( Effect.gen(function* denyUnevidencedReview() { const row = caseRow(); @@ -955,7 +955,7 @@ test('reviewer selection cannot waive missing subject/type evidence from a retai tenantId, }), ); - assert.equal(failure._tag, 'DuplicateCandidateConflict'); + assert.ok(Predicate.isTagged(failure, 'DuplicateCandidateConflict')); assert.equal(subject.inserts.length, 0); assert.equal(subject.updates.length, 0); }), diff --git a/app/verticals/party-registry/tests/unit/merge-alias-resolution-service.test.ts b/app/verticals/party-registry/tests/unit/merge-alias-resolution-service.test.ts index dc7b95be5..bfd2837e8 100644 --- a/app/verticals/party-registry/tests/unit/merge-alias-resolution-service.test.ts +++ b/app/verticals/party-registry/tests/unit/merge-alias-resolution-service.test.ts @@ -1,7 +1,7 @@ import { runEffectTestSync } from '@app/core-runtime/testing/effect-runtime'; import assert from 'node:assert/strict'; import test from 'node:test'; -import { Effect, Option } from 'effect'; +import { Effect, Option, Predicate } from 'effect'; import { makePartyAliasResolutionService } from '../../src/merge/party-alias-resolution.service.ts'; import type { PartyAliasLookup } from '../../src/merge/party-alias-resolution.service.ts'; @@ -20,7 +20,7 @@ const lookup = (overrides: Partial = {}): PartyAliasLookup => ...overrides, }); -test('central resolution service walks the complete canonical alias chain in one scoped transaction seam', () => { +void test('central resolution service walks the complete canonical alias chain in one scoped transaction seam', () => { const service = makePartyAliasResolutionService(lookup()); const result = runEffectTestSync(service.resolvePartyAlias(tenantId, 'party-b')); @@ -32,7 +32,7 @@ test('central resolution service walks the complete canonical alias chain in one }); }); -test('central resolution fails closed for cycles, cross-tenant targets, and broken chains', () => { +void test('central resolution fails closed for cycles, cross-tenant targets, and broken chains', () => { const cycle = makePartyAliasResolutionService( lookup({ findAlias: (_requestedTenantId, aliasPartyId) => @@ -46,7 +46,7 @@ test('central resolution fails closed for cycles, cross-tenant targets, and brok }), ); const cycleError = runEffectTestSync(Effect.flip(cycle.resolvePartyAlias(tenantId, 'party-a'))); - assert.equal(cycleError._tag, 'PartyAliasResolutionCycle'); + assert.ok(Predicate.isTagged(cycleError, 'PartyAliasResolutionCycle')); const crossTenant = makePartyAliasResolutionService( lookup({ @@ -63,7 +63,7 @@ test('central resolution fails closed for cycles, cross-tenant targets, and brok const crossTenantError = runEffectTestSync( Effect.flip(crossTenant.resolvePartyAlias(tenantId, 'party-b')), ); - assert.equal(crossTenantError._tag, 'PartyAliasResolutionCrossTenant'); + assert.ok(Predicate.isTagged(crossTenantError, 'PartyAliasResolutionCrossTenant')); const broken = makePartyAliasResolutionService( lookup({ @@ -72,16 +72,16 @@ test('central resolution fails closed for cycles, cross-tenant targets, and brok }), ); const brokenError = runEffectTestSync(Effect.flip(broken.resolvePartyAlias(tenantId, 'missing'))); - assert.equal(brokenError._tag, 'PartyAliasResolutionBrokenChain'); + assert.ok(Predicate.isTagged(brokenError, 'PartyAliasResolutionBrokenChain')); }); -test('central write guard returns typed canonical-survivor guidance and never forwards', () => { +void test('central write guard returns typed canonical-survivor guidance and never forwards', () => { const service = makePartyAliasResolutionService(lookup()); const rejection = runEffectTestSync( Effect.flip(service.requireCanonicalWriteTarget(tenantId, 'party-b')), ); - assert.equal(rejection._tag, 'PartyAliasWriteRejected'); + assert.ok(Predicate.isTagged(rejection, 'PartyAliasWriteRejected')); assert.deepEqual(rejection.aliasPartyRef, { moduleId: 'party.registry', resourceId: 'party-b', diff --git a/app/verticals/party-registry/tests/unit/merge-alias-resolution.test.ts b/app/verticals/party-registry/tests/unit/merge-alias-resolution.test.ts index a9e7f342a..85f3f712b 100644 --- a/app/verticals/party-registry/tests/unit/merge-alias-resolution.test.ts +++ b/app/verticals/party-registry/tests/unit/merge-alias-resolution.test.ts @@ -1,6 +1,6 @@ import assert from 'node:assert/strict'; import test from 'node:test'; -import { DateTime } from 'effect'; +import { DateTime, Predicate } from 'effect'; import type { PartyRef } from '../../shared/resources/party.ts'; import { assertCanonicalWriteTarget, @@ -26,7 +26,7 @@ const alias = (aliasPartyId: string, survivorPartyId: string, tenant = tenantId) survivorPartyRef: party(survivorPartyId, tenant), }); -test('resolves an historical alias chain to one final canonical Party', () => { +void test('resolves an historical alias chain to one final canonical Party', () => { const result = resolveCanonicalPartyRef(party('party-b'), [ alias('party-b', 'party-a'), alias('party-a', 'party-c'), @@ -40,30 +40,36 @@ test('resolves an historical alias chain to one final canonical Party', () => { }); }); -test('rejects alias cycles, self aliases, and cross-tenant targets', () => { - assert.equal( - resolveCanonicalPartyRef(party('party-a'), [ - alias('party-a', 'party-b'), - alias('party-b', 'party-a'), - ])._tag, - 'PartyAliasCycleRejected', +void test('rejects alias cycles, self aliases, and cross-tenant targets', () => { + assert.ok( + Predicate.isTagged( + resolveCanonicalPartyRef(party('party-a'), [ + alias('party-a', 'party-b'), + alias('party-b', 'party-a'), + ]), + 'PartyAliasCycleRejected', + ), ); - assert.equal( - resolveCanonicalPartyRef(party('party-a'), [alias('party-a', 'party-a')])._tag, - 'PartyAliasSelfReferenceRejected', + assert.ok( + Predicate.isTagged( + resolveCanonicalPartyRef(party('party-a'), [alias('party-a', 'party-a')]), + 'PartyAliasSelfReferenceRejected', + ), ); - assert.equal( - resolveCanonicalPartyRef(party('party-a'), [ - { - ...alias('party-a', 'party-b'), - survivorPartyRef: party('party-b', '22222222-2222-4222-8222-222222222222'), - }, - ])._tag, - 'PartyAliasCrossTenantRejected', + assert.ok( + Predicate.isTagged( + resolveCanonicalPartyRef(party('party-a'), [ + { + ...alias('party-a', 'party-b'), + survivorPartyRef: party('party-b', '22222222-2222-4222-8222-222222222222'), + }, + ]), + 'PartyAliasCrossTenantRejected', + ), ); }); -test('rejects new writes addressed to an absorbed alias instead of forwarding them', () => { +void test('rejects new writes addressed to an absorbed alias instead of forwarding them', () => { assert.deepEqual(assertCanonicalWriteTarget(party('party-b'), [alias('party-b', 'party-a')]), { _tag: 'AliasWriteRejected', aliasPartyRef: party('party-b'), diff --git a/app/verticals/party-registry/tests/unit/merge-survivor-selection.test.ts b/app/verticals/party-registry/tests/unit/merge-survivor-selection.test.ts index 0f497502d..4a8e6a001 100644 --- a/app/verticals/party-registry/tests/unit/merge-survivor-selection.test.ts +++ b/app/verticals/party-registry/tests/unit/merge-survivor-selection.test.ts @@ -1,6 +1,6 @@ import assert from 'node:assert/strict'; import test from 'node:test'; -import { Match } from 'effect'; +import { Match, Predicate } from 'effect'; import type { PartyRef } from '../../shared/resources/party.ts'; import type { MergeSurvivorCandidate, @@ -51,7 +51,7 @@ const expectSelected = (result: CanonicalSurvivorSelection) => Match.exhaustive, ); -test('blocks survivor selection when authoritative identity truth is unresolved', () => { +void test('blocks survivor selection when authoritative identity truth is unresolved', () => { const result = selectCanonicalSurvivor( confirmedSelection([ candidate('party-a'), @@ -66,7 +66,7 @@ test('blocks survivor selection when authoritative identity truth is unresolved' }); }); -test('uses the governed hierarchy before reference count, lifecycle, completeness, or age', () => { +void test('uses the governed hierarchy before reference count, lifecycle, completeness, or age', () => { const result = selectCanonicalSurvivor( confirmedSelection([ candidate('well-established', { @@ -84,7 +84,7 @@ test('uses the governed hierarchy before reference count, lifecycle, completenes ]), ); - assert.equal(result._tag, 'CanonicalSurvivorSelected'); + assert.ok(Predicate.isTagged(result, 'CanonicalSurvivorSelected')); const selected = expectSelected(result); assert.deepEqual(selected.survivorPartyRef, party('authoritative')); assert.equal(selected.decidingCriterion, 'AUTHORITATIVE_EVIDENCE'); @@ -96,7 +96,7 @@ test('uses the governed hierarchy before reference count, lifecycle, completenes ); }); -test('uses reference stability, lifecycle, completeness, age, then resource identity deterministically', () => { +void test('uses reference stability, lifecycle, completeness, age, then resource identity deterministically', () => { const referenceWinner = selectCanonicalSurvivor( confirmedSelection([ candidate('a', { referenceStabilityRank: 1 }), @@ -113,7 +113,7 @@ test('uses reference stability, lifecycle, completeness, age, then resource iden assert.equal(selected.decidingCriterion, 'STABLE_RESOURCE_IDENTITY'); }); -test('rejects a cross-tenant merge set before selection', () => { +void test('rejects a cross-tenant merge set before selection', () => { const result = selectCanonicalSurvivor( confirmedSelection([ candidate('party-a'), @@ -133,35 +133,37 @@ test('rejects a cross-tenant merge set before selection', () => { }); }); -test('rejects selection without an explicit confirmed duplicate decision and matching evidence set', () => { +void test('rejects selection without an explicit confirmed duplicate decision and matching evidence set', () => { const candidates = [candidate('party-a'), candidate('party-b')]; assert.deepEqual(selectCanonicalSurvivor({ candidates, confirmation: null }), { _tag: 'SurvivorSelectionBlocked', blocker: 'DUPLICATE_SET_NOT_CONFIRMED', conflictingPartyRefs: [party('party-a'), party('party-b')], }); - assert.equal( - selectCanonicalSurvivor({ - candidates, - confirmation: { - confirmedDuplicateDecisionId: ConfirmedDuplicateDecisionIdSchema.make('decision-1'), - confirmedPartyRefs: [party('party-a')], - decisionActorPrincipalId: DecisionActorPrincipalIdSchema.make('principal-1'), - evidenceRefs: ['evidence-1'], - }, - })._tag, - 'SurvivorSelectionBlocked', + assert.ok( + Predicate.isTagged( + selectCanonicalSurvivor({ + candidates, + confirmation: { + confirmedDuplicateDecisionId: ConfirmedDuplicateDecisionIdSchema.make('decision-1'), + confirmedPartyRefs: [party('party-a')], + decisionActorPrincipalId: DecisionActorPrincipalIdSchema.make('principal-1'), + evidenceRefs: ['evidence-1'], + }, + }), + 'SurvivorSelectionBlocked', + ), ); }); -test('retains immutable evaluated values and explains progressive elimination for three candidates', () => { +void test('retains immutable evaluated values and explains progressive elimination for three candidates', () => { const candidates = [ candidate('party-a', { authoritativeEvidenceRank: 3, referenceStabilityRank: 2 }), candidate('party-b', { authoritativeEvidenceRank: 3, referenceStabilityRank: 1 }), candidate('party-c', { authoritativeEvidenceRank: 1, referenceStabilityRank: 100 }), ]; const result = selectCanonicalSurvivor(confirmedSelection(candidates)); - assert.equal(result._tag, 'CanonicalSurvivorSelected'); + assert.ok(Predicate.isTagged(result, 'CanonicalSurvivorSelected')); const selected = expectSelected(result); const authority = selected.evidenceChain.find( ({ criterion }) => criterion === 'AUTHORITATIVE_EVIDENCE', diff --git a/app/verticals/party-registry/tests/unit/relationship-persistence.service.test.ts b/app/verticals/party-registry/tests/unit/relationship-persistence.service.test.ts index dd31b1f04..3e6428060 100644 --- a/app/verticals/party-registry/tests/unit/relationship-persistence.service.test.ts +++ b/app/verticals/party-registry/tests/unit/relationship-persistence.service.test.ts @@ -1,7 +1,7 @@ import { runEffectTestPromise } from '@app/core-runtime/testing/effect-runtime'; // @effect-diagnostics asyncFunction:off globalDate:off -- Existing compatibility boundary; expires: 2026-12-31. /* eslint-disable anti-slop/no-chained-type-assertions, anti-slop/no-unsafe-dictionary-type -- This focused harness models only the Drizzle system boundary used by the Relationship service. expires: 2026-12-31. */ -import { DateTime, Effect, Option, Schema } from 'effect'; +import { DateTime, Effect, Option, Schema, Predicate } from 'effect'; import assert from 'node:assert/strict'; import test from 'node:test'; import { PartyAliasWriteRejected } from '../../shared/domain/merge-alias-resolution.ts'; @@ -132,7 +132,7 @@ const transactionHarness = ( return { insertValues, transaction, updateSets }; }; -test('create persists an active assertion with unknown start and derives current state', async () => { +void test('create persists an active assertion with unknown start and derives current state', async () => { const created = relationshipRow(); const harness = transactionHarness( [ @@ -171,7 +171,7 @@ test('create persists an active assertion with unknown start and derives current assert.equal('isCurrent' in (harness.insertValues[0] ?? {}), false); }); -test('update refines an unknown historical validFrom through the persistence service', async () => { +void test('update refines an unknown historical validFrom through the persistence service', async () => { const refinedAt = '2025-01-01T00:00:00.000Z'; const validTo = new Date('2026-01-01T00:00:00.000Z'); const current = relationshipRow({ validTo }); @@ -201,7 +201,7 @@ test('update refines an unknown historical validFrom through the persistence ser assert.equal(harness.updateSets[0]?.['revision'], 2); }); -test('end keeps a future-ended relationship current and exposes bounded end history', async () => { +void test('end keeps a future-ended relationship current and exposes bounded end history', async () => { const effectiveAt = '2099-01-01T00:00:00.000Z'; const survivorId = '70000000-0000-4000-8000-000000000001'; const current = relationshipRow({ validFrom: new Date('2025-01-01T00:00:00.000Z') }); @@ -257,7 +257,7 @@ test('end keeps a future-ended relationship current and exposes bounded end hist assert.equal('isCurrent' in (harness.updateSets[0] ?? {}), false); }); -test('detail derives scheduled state and resolves stored endpoint aliases independently', async () => { +void test('detail derives scheduled state and resolves stored endpoint aliases independently', async () => { const canonicalFrom = '70000000-0000-4000-8000-000000000001'; const middleAlias = '80000000-0000-4000-8000-000000000001'; const scheduled = relationshipRow({ validFrom: new Date('2099-01-01T00:00:00.000Z') }); @@ -283,7 +283,7 @@ test('detail derives scheduled state and resolves stored endpoint aliases indepe assert.ok(Option.isNone(detail.to.requestedAlias)); }); -test('non-active assertions never read as current even with an open effective interval', async () => { +void test('non-active assertions never read as current even with an open effective interval', async () => { await Promise.all( ['RETRACTED', 'SUPERSEDED', 'DISPUTED'].map(async (assertionState) => { const harness = transactionHarness([ @@ -300,7 +300,7 @@ test('non-active assertions never read as current even with an open effective in ); }); -test('durable relationship update resolves alias-backed stored endpoints without rewriting them', async () => { +void test('durable relationship update resolves alias-backed stored endpoints without rewriting them', async () => { const survivorId = '70000000-0000-4000-8000-000000000001'; const updated = relationshipRow({ revision: 2, @@ -341,7 +341,7 @@ test('durable relationship update resolves alias-backed stored endpoints without assert.equal('fromPartyId' in (harness.updateSets[0] ?? {}), false); }); -test('create rejects an explicit alias endpoint with canonical survivor guidance', async () => { +void test('create rejects an explicit alias endpoint with canonical survivor guidance', async () => { const survivorId = '70000000-0000-4000-8000-000000000001'; const harness = transactionHarness([ [ @@ -373,14 +373,14 @@ test('create rejects an explicit alias endpoint with canonical survivor guidance ).pipe(Effect.flip), ); - assert.equal(rejection._tag, 'PartyAliasWriteRejected'); + assert.ok(Predicate.isTagged(rejection, 'PartyAliasWriteRejected')); if (Schema.is(PartyAliasWriteRejected)(rejection)) { assert.equal(rejection.canonicalPartyRef.resourceId, survivorId); } assert.equal(harness.insertValues.length, 0); }); -test('a known historical start cannot be rewritten by ordinary update', async () => { +void test('a known historical start cannot be rewritten by ordinary update', async () => { const harness = transactionHarness([ [relationshipRow({ validFrom: new Date('2025-01-01T00:00:00.000Z') })], ...canonicalEndpointReads, @@ -401,14 +401,14 @@ test('a known historical start cannot be rewritten by ordinary update', async () ).pipe(Effect.flip), ); - assert.equal(rejection._tag, 'PartyRelationshipCorrectionRequired'); + assert.ok(Predicate.isTagged(rejection, 'PartyRelationshipCorrectionRequired')); if (Schema.is(PartyRelationshipCorrectionRequired)(rejection)) { assert.equal(rejection.fact, 'validFrom'); } assert.equal(harness.updateSets.length, 0); }); -test('removing a future planned end clears its current evidence and retains prior audit detail', async () => { +void test('removing a future planned end clears its current evidence and retains prior audit detail', async () => { const current = relationshipRow({ endProvenanceMethod: 'MANUAL_CONFIRMATION', endProvenanceSource: 'ENGAGEMENT_REVIEW', @@ -448,7 +448,7 @@ test('removing a future planned end clears its current evidence and retains prio } }); -test('update can shorten a future planned end to a valid retrospective end with new evidence', async () => { +void test('update can shorten a future planned end to a valid retrospective end with new evidence', async () => { const validFrom = new Date('2025-01-01T00:00:00.000Z'); const effectiveAt = '2026-02-01T00:00:00.000Z'; const current = relationshipRow({ validFrom, validTo: new Date('2099-01-01T00:00:00.000Z') }); @@ -489,7 +489,7 @@ test('update can shorten a future planned end to a valid retrospective end with assert.equal(harness.updateSets[0]?.['endedByActionInvocationId'], actionInvocationId); }); -test('an evidence-backed end without a generic reason stays visible and retries exactly', async () => { +void test('an evidence-backed end without a generic reason stays visible and retries exactly', async () => { const effectiveAt = '2026-02-01T00:00:00.000Z'; const ended = relationshipRow({ endProvenanceMethod: 'DOCUMENT_REVIEW', diff --git a/app/verticals/party-registry/tests/unit/search-core-adapter.test.ts b/app/verticals/party-registry/tests/unit/search-core-adapter.test.ts index 7a27178c3..2ef551659 100644 --- a/app/verticals/party-registry/tests/unit/search-core-adapter.test.ts +++ b/app/verticals/party-registry/tests/unit/search-core-adapter.test.ts @@ -1,7 +1,7 @@ import { runEffectTestPromise } from '@app/core-runtime/testing/effect-runtime'; import assert from 'node:assert/strict'; import test from 'node:test'; -import { Effect, Schema } from 'effect'; +import { Effect, Schema, Predicate } from 'effect'; import { CoreSearchProjectionHitSchema } from '@app/core-runtime'; import type { CoreSearchQueryRuntimeService } from '@app/core-runtime'; import { makePartySearchProjectionGateway } from '../../src/search/parties.provider.ts'; @@ -59,7 +59,7 @@ const wrongResourceHit = Schema.decodeUnknownSync(CoreSearchProjectionHitSchema) title: 'Wrong', }); -test('Party adapter queries only the Core-owned Party projection and maps alias context', () => +void test('Party adapter queries only the Core-owned Party projection and maps alias context', () => runEffectTestPromise( Effect.gen(function* partyAdapterQuery() { const calls: unknown[] = []; @@ -93,7 +93,7 @@ test('Party adapter queries only the Core-owned Party projection and maps alias }), )); -test('Counterparty adapter uses trusted Legal Entity, effective time, role facet and safe periods', () => +void test('Counterparty adapter uses trusted Legal Entity, effective time, role facet and safe periods', () => runEffectTestPromise( Effect.gen(function* counterpartyAdapterQuery() { const calls: unknown[] = []; @@ -146,7 +146,7 @@ test('Counterparty adapter uses trusted Legal Entity, effective time, role facet }), )); -test('Party adapter fails closed when a generic projection returns the wrong resource contract', () => +void test('Party adapter fails closed when a generic projection returns the wrong resource contract', () => runEffectTestPromise( Effect.gen(function* invalidProjectionContract() { const core: CoreSearchQueryRuntimeService = { @@ -160,6 +160,6 @@ test('Party adapter fails closed when a generic projection returns the wrong res tenantId, }), ); - assert.equal(failure._tag, 'Failure'); + assert.ok(Predicate.isTagged(failure, 'Failure')); }), )); diff --git a/app/verticals/party-registry/tests/unit/search-projector.test.ts b/app/verticals/party-registry/tests/unit/search-projector.test.ts index b1ddadb5a..e45a72000 100644 --- a/app/verticals/party-registry/tests/unit/search-projector.test.ts +++ b/app/verticals/party-registry/tests/unit/search-projector.test.ts @@ -1,7 +1,7 @@ import { runEffectTestPromise } from '@app/core-runtime/testing/effect-runtime'; import assert from 'node:assert/strict'; import test from 'node:test'; -import { Effect, Exit, Match } from 'effect'; +import { Effect, Exit, Match, Predicate } from 'effect'; import { makeCoreSearchQueryRuntime, makeCoreSearchIngestion, @@ -68,7 +68,7 @@ const snapshot: PartySearchSourceSnapshot = { removedRefs: [], tenantId, }; -test('post-commit projection makes only active permission-safe identity evidence searchable', () => +void test('post-commit projection makes only active permission-safe identity evidence searchable', () => runEffectTestPromise( Effect.gen(function* testScenario() { const documents = yield* buildPartySearchDocuments(snapshot); @@ -100,7 +100,7 @@ test('post-commit projection makes only active permission-safe identity evidence ]); }), )); -test('aliases collapse to canonical identity and only alias-only evidence labels the match', () => +void test('aliases collapse to canonical identity and only alias-only evidence labels the match', () => runEffectTestPromise( Effect.gen(function* testScenario() { const [party] = snapshot.parties; @@ -158,7 +158,7 @@ const context: OutboxWorkerHandlerContext = { topic: 'party.registry.party-updated.v1', workerKey: 'party.registry.project-party-updated-to-search', }; -test('snapshot-generation replay is idempotent, archive/unarchive refreshes and older delivery cannot resurrect a tombstone', () => +void test('snapshot-generation replay is idempotent, archive/unarchive refreshes and older delivery cannot resurrect a tombstone', () => runEffectTestPromise( Effect.gen(function* testScenario() { const store = makeInMemoryCoreSearchProjectionStore(); @@ -217,7 +217,7 @@ test('snapshot-generation replay is idempotent, archive/unarchive refreshes and assert.deepEqual(yield* query(true), []); }), )); -test('future-ended contact disappears at its period boundary without another lifecycle message', () => +void test('future-ended contact disappears at its period boundary without another lifecycle message', () => runEffectTestPromise( Effect.gen(function* testScenario() { const [party] = snapshot.parties; @@ -261,7 +261,7 @@ test('future-ended contact disappears at its period boundary without another lif assert.deepEqual(yield* query('2026-09-04T00:00:00.000Z'), []); }), )); -test('Counterparty identity survives aliases, current-role expiry and canonical-party collisions', () => +void test('Counterparty identity survives aliases, current-role expiry and canonical-party collisions', () => runEffectTestPromise( Effect.gen(function* testScenario() { const legalEntityId = '20000000-0000-4000-8000-000000000002'; @@ -346,7 +346,7 @@ test('Counterparty identity survives aliases, current-role expiry and canonical- ); }), )); -test('shared public contact returns multiple Parties without uniqueness or matching authority', () => +void test('shared public contact returns multiple Parties without uniqueness or matching authority', () => runEffectTestPromise( Effect.gen(function* testScenario() { const store = makeInMemoryCoreSearchProjectionStore(); @@ -384,7 +384,7 @@ test('shared public contact returns multiple Parties without uniqueness or match ); }), )); -test('rebuild reconciles omitted documents and preserves tombstones against stale lifecycle delivery', () => +void test('rebuild reconciles omitted documents and preserves tombstones against stale lifecycle delivery', () => runEffectTestPromise( Effect.gen(function* testScenario() { const store = makeInMemoryCoreSearchProjectionStore(); @@ -426,7 +426,7 @@ test('rebuild reconciles omitted documents and preserves tombstones against stal ); }), )); -test('source failure is sanitized and leaves previously searchable state intact for retry', () => +void test('source failure is sanitized and leaves previously searchable state intact for retry', () => runEffectTestPromise( Effect.gen(function* testScenario() { const store = makeInMemoryCoreSearchProjectionStore(); @@ -462,11 +462,11 @@ test('source failure is sanitized and leaves previously searchable state intact resourceType: 'party.registry.party', tenantId, }); - assert.equal(failure._tag, 'Failure'); + assert.ok(Predicate.isTagged(failure, 'Failure')); assert.equal(priorHits.length, 1); }), )); -test('zero-length cancelled periods are never searchable and do not poison projection delivery', () => +void test('zero-length cancelled periods are never searchable and do not poison projection delivery', () => runEffectTestPromise( Effect.gen(function* testScenario() { const [party] = snapshot.parties; @@ -518,7 +518,7 @@ test('zero-length cancelled periods are never searchable and do not poison proje assert.deepEqual(result.value[1]?.temporalFacets, []); }), )); -test('projection generation is independent of an out-of-order business event sequence', () => +void test('projection generation is independent of an out-of-order business event sequence', () => runEffectTestPromise( Effect.gen(function* testScenario() { const store = makeInMemoryCoreSearchProjectionStore(); @@ -552,7 +552,7 @@ test('projection generation is independent of an out-of-order business event seq assert.equal(hits.length, 1); }), )); -test('correction and identifier/contact changes replace obsolete evidence instead of accumulating history', () => +void test('correction and identifier/contact changes replace obsolete evidence instead of accumulating history', () => runEffectTestPromise( Effect.gen(function* testScenario() { const [party] = snapshot.parties; @@ -605,7 +605,7 @@ test('correction and identifier/contact changes replace obsolete evidence instea assert.equal(corrected.length, 1); }), )); -test('a complete empty rebuild also rejects delayed evidence for a never-before-indexed Party', () => +void test('a complete empty rebuild also rejects delayed evidence for a never-before-indexed Party', () => runEffectTestPromise( Effect.gen(function* testScenario() { const store = makeInMemoryCoreSearchProjectionStore(); diff --git a/app/verticals/party-registry/tests/unit/search-rebuild-request.test.ts b/app/verticals/party-registry/tests/unit/search-rebuild-request.test.ts index 7e3ba5dc8..d739a1c9a 100644 --- a/app/verticals/party-registry/tests/unit/search-rebuild-request.test.ts +++ b/app/verticals/party-registry/tests/unit/search-rebuild-request.test.ts @@ -1,7 +1,7 @@ import { runEffectTestPromise } from '@app/core-runtime/testing/effect-runtime'; import assert from 'node:assert/strict'; import test from 'node:test'; -import { Effect, Schema } from 'effect'; +import { Effect, Schema, Predicate } from 'effect'; import type { OutboxWorkerHandlerContext } from '@app/core-runtime'; import { makeActionTestHarness } from '@app/core-runtime/testing/actions'; import { requestSearchRebuildAction } from '../../src/actions/request-search-rebuild.action.ts'; @@ -28,7 +28,7 @@ const request = { transport: { correlationId: 'search-rebuild-test', idempotencyKey: 'rebuild-1' }, }; -test('tenant rebuild requests require Party administration and canonical idempotency', () => { +void test('tenant rebuild requests require Party administration and canonical idempotency', () => { const { descriptor } = requestSearchRebuildAction; assert.equal(descriptor.actionKey, 'party.registry.request-search-rebuild'); assert.equal(descriptor.tenantPermission?.({}), 'manage_party_identity'); @@ -41,7 +41,7 @@ test('tenant rebuild requests require Party administration and canonical idempot ]); }); -test('authorized rebuild commits one linked request without reading identity or running the projector', () => { +void test('authorized rebuild commits one linked request without reading identity or running the projector', () => { const harness = makeActionTestHarness({ actionPermission: 'allowed', tenantPermission: 'allowed', @@ -79,7 +79,7 @@ test('authorized rebuild commits one linked request without reading identity or ); }); -test('denied Party administration cannot queue a rebuild even with Action execution permission', () => { +void test('denied Party administration cannot queue a rebuild even with Action execution permission', () => { const harness = makeActionTestHarness({ actionPermission: 'allowed', tenantPermission: 'denied', @@ -87,7 +87,7 @@ test('denied Party administration cannot queue a rebuild even with Action execut return runEffectTestPromise( Effect.gen(function* deniedRebuildRequest() { const error = yield* harness.runtime.runAction(request).pipe(Effect.flip); - assert.equal(error._tag, 'ActionPermissionDenied'); + assert.ok(Predicate.isTagged(error, 'ActionPermissionDenied')); const snapshot = harness.snapshot(); assert.deepEqual(snapshot.committed, []); assert.equal(snapshot.permissionDenials.length, 1); @@ -96,7 +96,7 @@ test('denied Party administration cannot queue a rebuild even with Action execut ); }); -test('replaying the same authorized rebuild request queues only once', () => { +void test('replaying the same authorized rebuild request queues only once', () => { const harness = makeActionTestHarness({ actionPermission: 'allowed', tenantPermission: 'allowed', @@ -105,7 +105,7 @@ test('replaying the same authorized rebuild request queues only once', () => { Effect.gen(function* replayRebuildRequest() { yield* harness.runtime.runAction(request); const replay = yield* harness.runtime.runAction(request).pipe(Effect.flip); - assert.equal(replay._tag, 'ActionAlreadyCommitted'); + assert.ok(Predicate.isTagged(replay, 'ActionAlreadyCommitted')); const snapshot = harness.snapshot(); assert.equal(snapshot.committed.length, 1); assert.equal(snapshot.committed[0]?.evidence.domainEvents.length, 1); @@ -128,7 +128,7 @@ const workerContext: OutboxWorkerHandlerContext = { workerKey: 'party.registry.rebuild-search', }; -test('rebuild worker uses its trusted committed context, and failures remain retryable', () => { +void test('rebuild worker uses its trusted committed context, and failures remain retryable', () => { const unavailable = new PartySearchProjectionUnavailable({ code: 'party_search_projection_unavailable', reason: 'Party search projection is temporarily unavailable', diff --git a/app/verticals/party-registry/tests/unit/search-semantics.test.ts b/app/verticals/party-registry/tests/unit/search-semantics.test.ts index b0afb63d8..868d646ee 100644 --- a/app/verticals/party-registry/tests/unit/search-semantics.test.ts +++ b/app/verticals/party-registry/tests/unit/search-semantics.test.ts @@ -1,6 +1,6 @@ import assert from 'node:assert/strict'; import test from 'node:test'; -import { Match } from 'effect'; +import { Match, Predicate } from 'effect'; import { normalizeCounterpartySearchHits, normalizePartySearchHits, @@ -40,7 +40,7 @@ const expectSearchResults = ( Match.exhaustive, ); -test('Party Search hides archived hits by default and explicitly labels included archived hits', () => { +void test('Party Search hides archived hits by default and explicitly labels included archived hits', () => { const hits: readonly PartySearchProjectionHit[] = [ { archived: false, canonicalPartyRef: partyRef('active'), title: 'Active' }, { archived: true, canonicalPartyRef: partyRef('archived'), title: 'Archived' }, @@ -51,14 +51,14 @@ test('Party Search hides archived hits by default and explicitly labels included items: [{ archived: false, matchedViaAlias: false, ref: partyRef('active'), title: 'Active' }], }); const included = normalizePartySearchHits({ includeArchived: true, tenantId }, hits); - assert.equal(included._tag, 'SearchResults'); + assert.ok(Predicate.isTagged(included, 'SearchResults')); assert.deepEqual( expectSearchResults(included).items.map(({ archived }) => archived), [false, true], ); }); -test('Party aliases collapse to one survivor while shared contact queries may retain multiple Parties', () => { +void test('Party aliases collapse to one survivor while shared contact queries may retain multiple Parties', () => { const survivor = partyRef('survivor'); const result = normalizePartySearchHits({ includeArchived: false, tenantId }, [ { archived: false, canonicalPartyRef: survivor, title: 'ACME' }, @@ -71,7 +71,7 @@ test('Party aliases collapse to one survivor while shared contact queries may re { archived: false, canonicalPartyRef: partyRef('shared-2'), title: 'Other person' }, ]); - assert.equal(result._tag, 'SearchResults'); + assert.ok(Predicate.isTagged(result, 'SearchResults')); const { items } = expectSearchResults(result); assert.deepEqual( items.map(({ ref }) => ref.resourceId), @@ -80,23 +80,27 @@ test('Party aliases collapse to one survivor while shared contact queries may re assert.equal(items[0]?.matchedViaAlias, true); }); -test('Party Search fails closed when Core returns a cross-tenant or inconsistent projection', () => { +void test('Party Search fails closed when Core returns a cross-tenant or inconsistent projection', () => { const wrongTenant = { ...partyRef('wrong'), tenantId: '90000000-0000-4000-8000-000000000009', }; - assert.equal( - normalizePartySearchHits({ includeArchived: true, tenantId }, [ - { archived: false, canonicalPartyRef: wrongTenant, title: 'Wrong' }, - ])._tag, - 'SearchProjectionViolation', + assert.ok( + Predicate.isTagged( + normalizePartySearchHits({ includeArchived: true, tenantId }, [ + { archived: false, canonicalPartyRef: wrongTenant, title: 'Wrong' }, + ]), + 'SearchProjectionViolation', + ), ); - assert.equal( - normalizePartySearchHits({ includeArchived: true, tenantId }, [ - { archived: false, canonicalPartyRef: partyRef('same'), title: 'One' }, - { archived: true, canonicalPartyRef: partyRef('same'), title: 'Two' }, - ])._tag, - 'SearchProjectionViolation', + assert.ok( + Predicate.isTagged( + normalizePartySearchHits({ includeArchived: true, tenantId }, [ + { archived: false, canonicalPartyRef: partyRef('same'), title: 'One' }, + { archived: true, canonicalPartyRef: partyRef('same'), title: 'Two' }, + ]), + 'SearchProjectionViolation', + ), ); }); @@ -113,7 +117,7 @@ const baseCounterpartyHit = ( rolePeriods, }); -test('Counterparty Search evaluates only current role periods at the exclusive time boundary', () => { +void test('Counterparty Search evaluates only current role periods at the exclusive time boundary', () => { const effectiveAt = '2026-09-03T12:00:00.000Z'; const hits: readonly CounterpartySearchProjectionHit[] = [ baseCounterpartyHit('ended', 'p1', [ @@ -139,7 +143,7 @@ test('Counterparty Search evaluates only current role periods at the exclusive t hits, ); - assert.equal(result._tag, 'SearchResults'); + assert.ok(Predicate.isTagged(result, 'SearchResults')); const { items } = expectSearchResults(result); assert.deepEqual( items.map(({ ref }) => ref.resourceId), @@ -148,7 +152,7 @@ test('Counterparty Search evaluates only current role periods at the exclusive t assert.deepEqual(items[1]?.currentRoles, ['CUSTOMER', 'SUPPLIER']); }); -test('Counterparty Search without a role retains durable Counterparties with no current role', () => { +void test('Counterparty Search without a role retains durable Counterparties with no current role', () => { const result = normalizeCounterpartySearchHits( { effectiveAt: '2026-09-03T12:00:00.000Z', @@ -159,11 +163,11 @@ test('Counterparty Search without a role retains durable Counterparties with no [baseCounterpartyHit('no-role', 'p1')], ); - assert.equal(result._tag, 'SearchResults'); + assert.ok(Predicate.isTagged(result, 'SearchResults')); assert.deepEqual(expectSearchResults(result).items[0]?.currentRoles, []); }); -test('Counterparty identity dedupes independently and survivor collisions are surfaced', () => { +void test('Counterparty identity dedupes independently and survivor collisions are surfaced', () => { const hits = [ baseCounterpartyHit('cp-1', 'survivor'), baseCounterpartyHit('cp-1', 'survivor'), @@ -179,7 +183,7 @@ test('Counterparty identity dedupes independently and survivor collisions are su hits, ); - assert.equal(result._tag, 'SearchResults'); + assert.ok(Predicate.isTagged(result, 'SearchResults')); const { items } = expectSearchResults(result); assert.deepEqual( items.map(({ ref }) => ref.resourceId), @@ -194,7 +198,7 @@ test('Counterparty identity dedupes independently and survivor collisions are su ); }); -test('Counterparty Search fails closed on the wrong Legal Entity instead of broadening scope', () => { +void test('Counterparty Search fails closed on the wrong Legal Entity instead of broadening scope', () => { const result = normalizeCounterpartySearchHits( { effectiveAt: '2026-09-03T12:00:00.000Z', @@ -213,5 +217,5 @@ test('Counterparty Search fails closed on the wrong Legal Entity instead of broa ], ); - assert.equal(result._tag, 'SearchProjectionViolation'); + assert.ok(Predicate.isTagged(result, 'SearchProjectionViolation')); }); diff --git a/app/verticals/party-registry/tests/unit/search-source.test.ts b/app/verticals/party-registry/tests/unit/search-source.test.ts index 718081662..0ba7b9e72 100644 --- a/app/verticals/party-registry/tests/unit/search-source.test.ts +++ b/app/verticals/party-registry/tests/unit/search-source.test.ts @@ -7,7 +7,7 @@ import { runEffectTestPromise } from '@app/core-runtime/testing/effect-runtime'; import type { AnyColumn, Query, SQL, Table } from 'drizzle-orm'; import { getTableName } from 'drizzle-orm'; import { PgDialect } from 'drizzle-orm/pg-core'; -import { DateTime, Effect, Result } from 'effect'; +import { DateTime, Effect, Result, Predicate } from 'effect'; import assert from 'node:assert/strict'; import test from 'node:test'; import { makePartySearchProjectionSource } from '../../src/services/party-search-projection-source.service.ts'; @@ -80,7 +80,7 @@ const harness = ( return { columns, filters, scopes, source: makePartySearchProjectionSource(snapshot) }; }; -test('canonical snapshot preserves alias identity and legal-entity Counterparty context', () => +void test('canonical snapshot preserves alias identity and legal-entity Counterparty context', () => runEffectTestPromise( Effect.gen(function* canonicalAliasSnapshot() { const { source } = harness({ @@ -144,7 +144,7 @@ test('canonical snapshot preserves alias identity and legal-entity Counterparty }), )); -test('source exposes only current public email and phone search evidence, never ADDRESS or raw contact fields', () => +void test('source exposes only current public email and phone search evidence, never ADDRESS or raw contact fields', () => runEffectTestPromise( Effect.gen(function* privateSearchEvidence() { const contact = { @@ -220,7 +220,7 @@ test('source exposes only current public email and phone search evidence, never }), )); -test('missing Party and Counterparty targets produce explicit versioned tombstone refs', () => +void test('missing Party and Counterparty targets produce explicit versioned tombstone refs', () => runEffectTestPromise( Effect.gen(function* missingTargetTombstones() { const { source } = harness({}); @@ -239,7 +239,7 @@ test('missing Party and Counterparty targets produce explicit versioned tombston }), )); -test('full rebuild reads each Core-enumerated legal entity in the same snapshot and keeps distinct Counterparties', () => +void test('full rebuild reads each Core-enumerated legal entity in the same snapshot and keeps distinct Counterparties', () => runEffectTestPromise( Effect.gen(function* rebuildSnapshot() { const secondLegalEntityId = '30000000-0000-4000-8000-000000000002'; @@ -270,7 +270,7 @@ test('full rebuild reads each Core-enumerated legal entity in the same snapshot }), )); -test('Counterparty-only refresh emits only its canonical family and selected Counterparty', () => +void test('Counterparty-only refresh emits only its canonical family and selected Counterparty', () => runEffectTestPromise( Effect.gen(function* targetedCounterpartySnapshot() { const otherId = '20000000-0000-4000-8000-000000000009'; @@ -301,7 +301,7 @@ test('Counterparty-only refresh emits only its canonical family and selected Cou }), )); -test('alias cycles and cross-tenant source rows fail closed with sanitized typed failures', () => +void test('alias cycles and cross-tenant source rows fail closed with sanitized typed failures', () => runEffectTestPromise( Effect.gen(function* rejectedSourceSnapshot() { for (const rows of [ @@ -319,7 +319,7 @@ test('alias cycles and cross-tenant source rows fail closed with sanitized typed const outcome = yield* source.load(context, { rebuild: true }).pipe(Effect.result); assert.ok(Result.isFailure(outcome)); if (Result.isFailure(outcome)) { - assert.equal(outcome.failure._tag, 'PartySearchProjectionUnavailable'); + assert.ok(Predicate.isTagged(outcome.failure, 'PartySearchProjectionUnavailable')); assert.doesNotMatch(outcome.failure.reason, /Secret name|foreign-tenant/u); } }