feat: 기능 추가 과정중
This commit is contained in:
@@ -0,0 +1,252 @@
|
||||
import {
|
||||
mappingSuccess,
|
||||
type InstalledBoundaryMapper,
|
||||
} from "../../../src/contracts/boundary-mapper.ts";
|
||||
import type { ApiOperation } from "../../../src/contracts/api-operations.ts";
|
||||
import {
|
||||
createRealtimePolicyRegistry,
|
||||
defineEventTypeId,
|
||||
defineExternalEventEffectProfileId,
|
||||
defineRealtimeEndpointId,
|
||||
defineRealtimeKillSwitchId,
|
||||
defineStreamRegistrationId,
|
||||
type RealtimeEventTypeRegistration,
|
||||
type RealtimeLimits,
|
||||
type RealtimePolicyRegistry,
|
||||
type RealtimeRecoveryProfile,
|
||||
type RealtimeStreamRegistration,
|
||||
} from "../../../src/contracts/realtime-streams.ts";
|
||||
import type { RuntimeSchemaCodec } from "../../../src/contracts/schema-registry.ts";
|
||||
import {
|
||||
createRealtimeEventCodec,
|
||||
type RealtimeEventCodec,
|
||||
} from "../../../src/adapters/realtime/event-codec.ts";
|
||||
|
||||
export const STREAM_ID = defineStreamRegistrationId("REFERENCE_STREAM");
|
||||
export const EVENT_TYPE = defineEventTypeId("REFERENCE_CHANGED");
|
||||
export const ENDPOINT_ID = defineRealtimeEndpointId("REFERENCE_ENDPOINT");
|
||||
export const EFFECT_PROFILE_ID =
|
||||
defineExternalEventEffectProfileId("REFERENCE_INVALIDATE");
|
||||
export const KILL_SWITCH_ID =
|
||||
defineRealtimeKillSwitchId("REFERENCE_KILL_SWITCH");
|
||||
|
||||
export const TEST_LIMITS: RealtimeLimits = Object.freeze({
|
||||
maxEventBytes: 4_096,
|
||||
maxPayloadDepth: 8,
|
||||
maxPayloadNodes: 128,
|
||||
maxQueueEvents: 8,
|
||||
maxQueueBytes: 32_768,
|
||||
maxDedupeEntries: 16,
|
||||
maxDedupeBytes: 32_768,
|
||||
dedupeTtlMs: 60_000,
|
||||
});
|
||||
|
||||
const eventPayloadCodec: RuntimeSchemaCodec = Object.freeze({
|
||||
schemaId: "ReferenceRealtimePayload",
|
||||
parse(value) {
|
||||
if (
|
||||
!value ||
|
||||
typeof value !== "object" ||
|
||||
Array.isArray(value) ||
|
||||
Object.keys(value).length !== 1 ||
|
||||
typeof (value as Readonly<Record<string, unknown>>).value !== "string"
|
||||
) {
|
||||
return {
|
||||
success: false,
|
||||
issues: [{ path: "value", code: "INVALID_TYPE" }],
|
||||
};
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
value: (value as Readonly<Record<string, string>>).value,
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
const checkpointCodec: RuntimeSchemaCodec = Object.freeze({
|
||||
schemaId: "ReferenceRealtimeCheckpoint",
|
||||
parse: (value) => ({ success: true, data: value }),
|
||||
});
|
||||
|
||||
export const TEST_SCHEMA_CODECS = Object.freeze({
|
||||
ReferenceRealtimePayload: eventPayloadCodec,
|
||||
ReferenceRealtimeCheckpoint: checkpointCodec,
|
||||
});
|
||||
|
||||
export const TEST_MAPPER: InstalledBoundaryMapper = Object.freeze({
|
||||
mapperId: "ReferenceRealtimeMapper",
|
||||
mapperVersion: 1,
|
||||
inputSchemaId: "ReferenceRealtimePayload",
|
||||
outputContractId: "ReferenceRealtimeEvent",
|
||||
owner: "reference-feature",
|
||||
maxOutputItems: 1,
|
||||
map(input) {
|
||||
if (
|
||||
!input ||
|
||||
typeof input !== "object" ||
|
||||
typeof (input as Readonly<Record<string, unknown>>).value !== "string"
|
||||
) {
|
||||
return { ok: false, code: "MAPPING_INVARIANT_REJECTED" };
|
||||
}
|
||||
return mappingSuccess(
|
||||
Object.freeze({
|
||||
value: (input as Readonly<Record<string, string>>).value,
|
||||
}),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export const TEST_MAPPERS = Object.freeze({
|
||||
ReferenceRealtimeMapper: TEST_MAPPER,
|
||||
});
|
||||
|
||||
const snapshotOperation: ApiOperation = Object.freeze({
|
||||
method: "GET",
|
||||
path: "/api/reference-snapshot",
|
||||
operationId: "GET_REFERENCE_REALTIME_SNAPSHOT",
|
||||
auth: "external-session",
|
||||
timeoutMs: 10_000,
|
||||
idempotency: "safe",
|
||||
retry: "never",
|
||||
requestSource: "none",
|
||||
requestSchema: "NoRequest",
|
||||
responseSchema: "ReferenceRealtimeCheckpoint",
|
||||
owner: "reference-feature",
|
||||
contractVersion: 2,
|
||||
protocol: "REST",
|
||||
semantics: "QUERY",
|
||||
replayPolicy: "SAFE",
|
||||
idempotencyKeyPolicy: "NONE",
|
||||
mapperId: "ReferenceRealtimeSnapshotMapper",
|
||||
successStatuses: [200],
|
||||
responseMediaTypes: ["application/json"],
|
||||
maxResponseBytes: 16_384,
|
||||
providerId: "PRIMARY_API",
|
||||
authProfileId: "EXTERNAL_SESSION",
|
||||
csrfProfileId: "NONE",
|
||||
pathSchema: "NoRequest",
|
||||
pathParameterNames: [],
|
||||
maxEncodedSearchBytes: 0,
|
||||
});
|
||||
|
||||
export const TEST_API_OPERATIONS = Object.freeze({
|
||||
GET_REFERENCE_REALTIME_SNAPSHOT: snapshotOperation,
|
||||
});
|
||||
|
||||
export type TestRegistryOptions = Readonly<{
|
||||
recovery?: RealtimeRecoveryProfile;
|
||||
delivery?: RealtimeStreamRegistration["delivery"];
|
||||
stateBearing?: boolean;
|
||||
limits?: RealtimeLimits;
|
||||
streamMutator?: (
|
||||
stream: RealtimeStreamRegistration,
|
||||
) => RealtimeStreamRegistration;
|
||||
eventTypeMutator?: (
|
||||
eventType: RealtimeEventTypeRegistration,
|
||||
) => RealtimeEventTypeRegistration;
|
||||
}>;
|
||||
|
||||
export function createTestRealtimeRegistry(
|
||||
options: TestRegistryOptions = {},
|
||||
): RealtimePolicyRegistry {
|
||||
const recovery =
|
||||
options.recovery ??
|
||||
({
|
||||
mode: "CURSOR",
|
||||
snapshotOperationId: "GET_REFERENCE_REALTIME_SNAPSHOT",
|
||||
checkpointCodecId: "ReferenceRealtimeCheckpoint",
|
||||
barrier: "REPLAY",
|
||||
} as const);
|
||||
const eventType: RealtimeEventTypeRegistration = {
|
||||
id: EVENT_TYPE,
|
||||
owner: "reference-feature",
|
||||
payloadSchemaId: "ReferenceRealtimePayload",
|
||||
mapperId: "ReferenceRealtimeMapper",
|
||||
effectProfileId: EFFECT_PROFILE_ID,
|
||||
stateBearing: options.stateBearing ?? true,
|
||||
};
|
||||
const stream: RealtimeStreamRegistration = {
|
||||
id: STREAM_ID,
|
||||
protocol: "REALTIME_EVENT_V1",
|
||||
owner: "reference-feature",
|
||||
scope: "ACCOUNT_BOUND",
|
||||
primaryTransport: "SSE",
|
||||
endpointId: ENDPOINT_ID,
|
||||
eventTypeIds: [EVENT_TYPE],
|
||||
delivery: options.delivery ?? "AUTHORITATIVE_DELTA",
|
||||
recovery,
|
||||
fallback:
|
||||
recovery.mode === "SESSION_REBUILD"
|
||||
? "EXPLICITLY_STALE"
|
||||
: "BOUNDED_POLLING",
|
||||
hiddenPolicy: "CLOSE",
|
||||
limits: options.limits ?? TEST_LIMITS,
|
||||
killSwitchId: KILL_SWITCH_ID,
|
||||
};
|
||||
return createRealtimePolicyRegistry({
|
||||
streams: [options.streamMutator?.(stream) ?? stream],
|
||||
eventTypes: [
|
||||
options.eventTypeMutator?.(eventType) ?? eventType,
|
||||
],
|
||||
bindings: {
|
||||
schemaCodecs: TEST_SCHEMA_CODECS,
|
||||
mappers: TEST_MAPPERS,
|
||||
apiOperations: TEST_API_OPERATIONS,
|
||||
endpointIds: [ENDPOINT_ID],
|
||||
effectProfileIds: [EFFECT_PROFILE_ID],
|
||||
killSwitchIds: [KILL_SWITCH_ID],
|
||||
rebuildInputIds: ["referenceRealtimeRebuild"],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function createTestRealtimeCodec(
|
||||
registry = createTestRealtimeRegistry(),
|
||||
): RealtimeEventCodec {
|
||||
return createRealtimeEventCodec({
|
||||
registry,
|
||||
schemaCodecs: TEST_SCHEMA_CODECS,
|
||||
});
|
||||
}
|
||||
|
||||
export type EventOverrides = Readonly<{
|
||||
protocol?: unknown;
|
||||
streamId?: unknown;
|
||||
streamEpoch?: unknown;
|
||||
eventType?: unknown;
|
||||
eventId?: unknown;
|
||||
sequence?: unknown;
|
||||
recoveryMode?: unknown;
|
||||
resumeCursor?: unknown;
|
||||
occurredAt?: unknown;
|
||||
scopeBinding?: unknown;
|
||||
payload?: unknown;
|
||||
}>;
|
||||
|
||||
export function realtimeEventValue(
|
||||
overrides: EventOverrides = {},
|
||||
): Readonly<Record<string, unknown>> {
|
||||
return {
|
||||
protocol: "REALTIME_EVENT_V1",
|
||||
streamId: STREAM_ID,
|
||||
streamEpoch: "stream-epoch-0001",
|
||||
eventType: EVENT_TYPE,
|
||||
eventId: "event-00000001",
|
||||
sequence: "1",
|
||||
recoveryMode: "CURSOR",
|
||||
resumeCursor: "cursor-00000001",
|
||||
occurredAt: "2026-07-28T01:02:03.123Z",
|
||||
scopeBinding: "scope-binding-0001",
|
||||
payload: { value: "changed" },
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
export function realtimeEventJson(
|
||||
overrides: EventOverrides = {},
|
||||
): string {
|
||||
return JSON.stringify(realtimeEventValue(overrides));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user