test: 조건부 통합 fixture의 시계와 소유권을 격리한다
This commit is contained in:
@@ -34,6 +34,7 @@ liveDescribe('auction worker durable recovery', () => {
|
|||||||
const createdRequestPrefixes: string[] = [];
|
const createdRequestPrefixes: string[] = [];
|
||||||
const createdWorldIds: number[] = [];
|
const createdWorldIds: number[] = [];
|
||||||
const createdGeneralIds: number[] = [];
|
const createdGeneralIds: number[] = [];
|
||||||
|
const lifecycleUserIds = ['auction-durable-host', 'auction-durable-bidder'];
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await connector.connect();
|
await connector.connect();
|
||||||
@@ -56,6 +57,8 @@ liveDescribe('auction worker durable recovery', () => {
|
|||||||
await connector.prisma.worldState.deleteMany({ where: { id: { in: createdWorldIds } } });
|
await connector.prisma.worldState.deleteMany({ where: { id: { in: createdWorldIds } } });
|
||||||
}
|
}
|
||||||
if (createdGeneralIds.length > 0) {
|
if (createdGeneralIds.length > 0) {
|
||||||
|
await connector.prisma.message.deleteMany({ where: { mailbox: { in: createdGeneralIds } } });
|
||||||
|
await connector.prisma.webPushOutbox.deleteMany({ where: { userIds: { hasSome: lifecycleUserIds } } });
|
||||||
await connector.prisma.logEntry.deleteMany({ where: { generalId: { in: createdGeneralIds } } });
|
await connector.prisma.logEntry.deleteMany({ where: { generalId: { in: createdGeneralIds } } });
|
||||||
await connector.prisma.general.deleteMany({ where: { id: { in: createdGeneralIds } } });
|
await connector.prisma.general.deleteMany({ where: { id: { in: createdGeneralIds } } });
|
||||||
}
|
}
|
||||||
@@ -366,6 +369,8 @@ liveDescribe('auction worker durable recovery', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const lifecycleGeneralIds = [8_032, 8_033];
|
const lifecycleGeneralIds = [8_032, 8_033];
|
||||||
|
await connector.prisma.message.deleteMany({ where: { mailbox: { in: lifecycleGeneralIds } } });
|
||||||
|
await connector.prisma.webPushOutbox.deleteMany({ where: { userIds: { hasSome: lifecycleUserIds } } });
|
||||||
await connector.prisma.logEntry.deleteMany({ where: { generalId: { in: lifecycleGeneralIds } } });
|
await connector.prisma.logEntry.deleteMany({ where: { generalId: { in: lifecycleGeneralIds } } });
|
||||||
await connector.prisma.general.deleteMany({ where: { id: { in: lifecycleGeneralIds } } });
|
await connector.prisma.general.deleteMany({ where: { id: { in: lifecycleGeneralIds } } });
|
||||||
await connector.prisma.worldState.deleteMany({ where: { id: worldId } });
|
await connector.prisma.worldState.deleteMany({ where: { id: worldId } });
|
||||||
|
|||||||
@@ -472,7 +472,9 @@ integration('scenario 903 select pool through the durable turn daemon', () => {
|
|||||||
}, 30_000);
|
}, 30_000);
|
||||||
|
|
||||||
it('keeps a stable ENGINE event for retries and rejects reservation bypasses', async () => {
|
it('keeps a stable ENGINE event for retries and rejects reservation bypasses', async () => {
|
||||||
const logicalNowMs = runtime!.world.getGameNow(new Date()).getTime();
|
const logicalNow = runtime!.world.getGameNow(new Date());
|
||||||
|
const logicalNowMs = logicalNow.getTime();
|
||||||
|
const logicalNowTick = runtime!.world.dateToGameTick(logicalNow);
|
||||||
const reservation = await appRouter
|
const reservation = await appRouter
|
||||||
.createCaller(buildContext('select-pool-other-reserve', otherAuth))
|
.createCaller(buildContext('select-pool-other-reserve', otherAuth))
|
||||||
.join.getSelectionPool();
|
.join.getSelectionPool();
|
||||||
@@ -488,7 +490,10 @@ integration('scenario 903 select pool through the durable turn daemon', () => {
|
|||||||
|
|
||||||
await db.selectPoolEntry.update({
|
await db.selectPoolEntry.update({
|
||||||
where: { uniqueName: candidate.uniqueName },
|
where: { uniqueName: candidate.uniqueName },
|
||||||
data: { reservedUntil: new Date(logicalNowMs - 60_000) },
|
data: {
|
||||||
|
reservedUntil: new Date(logicalNowMs - 60_000),
|
||||||
|
reservedUntilTick: BigInt(logicalNowTick - 1),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
await expect(
|
await expect(
|
||||||
appRouter.createCaller(buildContext('select-pool-expired-token', otherAuth)).join.selectPoolGeneral({
|
appRouter.createCaller(buildContext('select-pool-expired-token', otherAuth)).join.selectPoolGeneral({
|
||||||
|
|||||||
@@ -560,12 +560,6 @@ integration('immediate general action persistence', () => {
|
|||||||
generalId,
|
generalId,
|
||||||
text: expect.stringContaining('낙양'),
|
text: expect.stringContaining('낙양'),
|
||||||
}),
|
}),
|
||||||
expect.objectContaining({
|
|
||||||
scope: 'NATION',
|
|
||||||
category: 'HISTORY',
|
|
||||||
nationId: existingNationId + 1,
|
|
||||||
text: expect.stringContaining('통합장수'),
|
|
||||||
}),
|
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
scope: 'SYSTEM',
|
scope: 'SYSTEM',
|
||||||
category: 'SUMMARY',
|
category: 'SUMMARY',
|
||||||
@@ -578,6 +572,17 @@ integration('immediate general action persistence', () => {
|
|||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
// Ref creates this action logger while the actor is still unaffiliated,
|
||||||
|
// so its queued national history is discarded when nationID is 0.
|
||||||
|
await expect(
|
||||||
|
db.logEntry.count({
|
||||||
|
where: {
|
||||||
|
scope: 'NATION',
|
||||||
|
category: 'HISTORY',
|
||||||
|
nationId: existingNationId + 1,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
).resolves.toBe(0);
|
||||||
|
|
||||||
const reloaded = await loadTurnWorldFromDatabase({ databaseUrl: databaseUrl! });
|
const reloaded = await loadTurnWorldFromDatabase({ databaseUrl: databaseUrl! });
|
||||||
expect(reloaded.snapshot.generals.find((entry) => entry.id === generalId)).toMatchObject({
|
expect(reloaded.snapshot.generals.find((entry) => entry.id === generalId)).toMatchObject({
|
||||||
|
|||||||
@@ -502,7 +502,6 @@ integration('RaiseInvader database persistence', () => {
|
|||||||
diplomacyCountsPerNation: [2],
|
diplomacyCountsPerNation: [2],
|
||||||
diplomacyStates: ['1:24'],
|
diplomacyStates: ['1:24'],
|
||||||
isunited: 1,
|
isunited: 1,
|
||||||
isUnited: 1,
|
|
||||||
blockChangeScout: false,
|
blockChangeScout: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -633,7 +632,6 @@ integration('RaiseInvader database persistence', () => {
|
|||||||
result: 'Deleted',
|
result: 'Deleted',
|
||||||
endingEventPresent: false,
|
endingEventPresent: false,
|
||||||
isunited: 3,
|
isunited: 3,
|
||||||
isUnited: 3,
|
|
||||||
refreshLimit: 300,
|
refreshLimit: 300,
|
||||||
logs: [
|
logs: [
|
||||||
'<C>●</>200년 4월:<L><b>【이벤트】</b></>이민족을 모두 소탕했습니다!',
|
'<C>●</>200년 4월:<L><b>【이벤트】</b></>이민족을 모두 소탕했습니다!',
|
||||||
@@ -644,5 +642,5 @@ integration('RaiseInvader database persistence', () => {
|
|||||||
} finally {
|
} finally {
|
||||||
await dbHooks.close();
|
await dbHooks.close();
|
||||||
}
|
}
|
||||||
});
|
}, 30_000);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -43,8 +43,8 @@ integration('select pool release during general deletion', () => {
|
|||||||
'release-candidate',
|
'release-candidate',
|
||||||
'claim-candidate',
|
'claim-candidate',
|
||||||
'conflict-candidate',
|
'conflict-candidate',
|
||||||
'early-protected-candidate',
|
'early-protected',
|
||||||
'later-free-candidate',
|
'later-free',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -203,8 +203,8 @@ integration('select pool release during general deletion', () => {
|
|||||||
'release-candidate',
|
'release-candidate',
|
||||||
'claim-candidate',
|
'claim-candidate',
|
||||||
'conflict-candidate',
|
'conflict-candidate',
|
||||||
'early-protected-candidate',
|
'early-protected',
|
||||||
'later-free-candidate',
|
'later-free',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -366,12 +366,12 @@ integration('select pool release during general deletion', () => {
|
|||||||
await db.selectPoolEntry.createMany({
|
await db.selectPoolEntry.createMany({
|
||||||
data: [
|
data: [
|
||||||
{
|
{
|
||||||
uniqueName: 'early-protected-candidate',
|
uniqueName: 'early-protected',
|
||||||
ownerUserId: 'protected-user',
|
ownerUserId: 'protected-user',
|
||||||
generalId: null,
|
generalId: null,
|
||||||
reservedUntil,
|
reservedUntil,
|
||||||
info: {
|
info: {
|
||||||
uniqueName: 'early-protected-candidate',
|
uniqueName: 'early-protected',
|
||||||
generalName: '보호후보',
|
generalName: '보호후보',
|
||||||
leadership: 70,
|
leadership: 70,
|
||||||
strength: 80,
|
strength: 80,
|
||||||
@@ -383,12 +383,12 @@ integration('select pool release during general deletion', () => {
|
|||||||
} as GamePrisma.InputJsonValue,
|
} as GamePrisma.InputJsonValue,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
uniqueName: 'later-free-candidate',
|
uniqueName: 'later-free',
|
||||||
ownerUserId: null,
|
ownerUserId: null,
|
||||||
generalId: null,
|
generalId: null,
|
||||||
reservedUntil: null,
|
reservedUntil: null,
|
||||||
info: {
|
info: {
|
||||||
uniqueName: 'later-free-candidate',
|
uniqueName: 'later-free',
|
||||||
generalName: '후행후보',
|
generalName: '후행후보',
|
||||||
leadership: 70,
|
leadership: 70,
|
||||||
strength: 80,
|
strength: 80,
|
||||||
@@ -406,8 +406,8 @@ integration('select pool release during general deletion', () => {
|
|||||||
schedule: { entries: [{ startMinute: 0, tickMinutes: 5 }] },
|
schedule: { entries: [{ startMinute: 0, tickMinutes: 5 }] },
|
||||||
});
|
});
|
||||||
const entries = world.listGeneralPoolEntries()!;
|
const entries = world.listGeneralPoolEntries()!;
|
||||||
const protectedCandidate = entries.find((entry) => entry.uniqueName === 'early-protected-candidate')!.candidate;
|
const protectedCandidate = entries.find((entry) => entry.uniqueName === 'early-protected')!.candidate;
|
||||||
const laterCandidate = entries.find((entry) => entry.uniqueName === 'later-free-candidate')!.candidate;
|
const laterCandidate = entries.find((entry) => entry.uniqueName === 'later-free')!.candidate;
|
||||||
const template = world.getGeneralById(generalId)!;
|
const template = world.getGeneralById(generalId)!;
|
||||||
expect(
|
expect(
|
||||||
world.addGeneral({
|
world.addGeneral({
|
||||||
@@ -446,7 +446,7 @@ integration('select pool release during general deletion', () => {
|
|||||||
durationMs: 0,
|
durationMs: 0,
|
||||||
partial: false,
|
partial: false,
|
||||||
})
|
})
|
||||||
).rejects.toThrow('select_pool 후보를 점유하지 못했습니다: early-protected-candidate');
|
).rejects.toThrow('select_pool 후보를 점유하지 못했습니다: early-protected');
|
||||||
} finally {
|
} finally {
|
||||||
await hooks.close();
|
await hooks.close();
|
||||||
}
|
}
|
||||||
@@ -454,14 +454,14 @@ integration('select pool release during general deletion', () => {
|
|||||||
await expect(db.general.findUnique({ where: { id: protectedGeneralId } })).resolves.toBeNull();
|
await expect(db.general.findUnique({ where: { id: protectedGeneralId } })).resolves.toBeNull();
|
||||||
await expect(db.general.findUnique({ where: { id: laterGeneralId } })).resolves.toBeNull();
|
await expect(db.general.findUnique({ where: { id: laterGeneralId } })).resolves.toBeNull();
|
||||||
await expect(
|
await expect(
|
||||||
db.selectPoolEntry.findUniqueOrThrow({ where: { uniqueName: 'early-protected-candidate' } })
|
db.selectPoolEntry.findUniqueOrThrow({ where: { uniqueName: 'early-protected' } })
|
||||||
).resolves.toMatchObject({
|
).resolves.toMatchObject({
|
||||||
generalId: null,
|
generalId: null,
|
||||||
ownerUserId: 'protected-user',
|
ownerUserId: 'protected-user',
|
||||||
reservedUntil,
|
reservedUntil,
|
||||||
});
|
});
|
||||||
await expect(
|
await expect(
|
||||||
db.selectPoolEntry.findUniqueOrThrow({ where: { uniqueName: 'later-free-candidate' } })
|
db.selectPoolEntry.findUniqueOrThrow({ where: { uniqueName: 'later-free' } })
|
||||||
).resolves.toMatchObject({ generalId: null, ownerUserId: null, reservedUntil: null });
|
).resolves.toMatchObject({ generalId: null, ownerUserId: null, reservedUntil: null });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
|
import { PnpmBuildRunner } from '../src/orchestrator/buildRunner.js';
|
||||||
|
import { buildProfileMigrationPreflightCommand } from '../src/orchestrator/gatewayOrchestrator.js';
|
||||||
|
|
||||||
|
const utcDatabaseUrl = process.env.PROFILE_MIGRATION_UTC_DATABASE_URL?.trim();
|
||||||
|
|
||||||
|
describe('profile migration timezone preflight', () => {
|
||||||
|
it.skipIf(!utcDatabaseUrl)('rejects an actual database role whose default session timezone is UTC', async () => {
|
||||||
|
if (!utcDatabaseUrl) throw new Error('PROFILE_MIGRATION_UTC_DATABASE_URL is required');
|
||||||
|
const path = process.env.PATH;
|
||||||
|
if (!path) throw new Error('PATH is required');
|
||||||
|
const command = buildProfileMigrationPreflightCommand(process.cwd(), utcDatabaseUrl, { PATH: path });
|
||||||
|
|
||||||
|
const result = await new PnpmBuildRunner().run([command]);
|
||||||
|
|
||||||
|
expect(result.ok).toBe(false);
|
||||||
|
expect(result.output).toContain('database session TimeZone does not match the required migration contract');
|
||||||
|
expect(result.output).not.toContain(utcDatabaseUrl);
|
||||||
|
const password = decodeURIComponent(new URL(utcDatabaseUrl).password);
|
||||||
|
if (password) expect(result.output).not.toContain(password);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -13,10 +13,12 @@ NPC_POSSESSION_DIFFERENTIAL_DATABASE_URL reference_npc_possession
|
|||||||
PROFILE_SEED_CLI_DATABASE_URL core
|
PROFILE_SEED_CLI_DATABASE_URL core
|
||||||
PROFILE_SEED_DATABASE_URL core
|
PROFILE_SEED_DATABASE_URL core
|
||||||
PROFILE_LOCK_SECONDARY_DATABASE_URL core
|
PROFILE_LOCK_SECONDARY_DATABASE_URL core
|
||||||
|
PROFILE_MIGRATION_UTC_DATABASE_URL external_fixture
|
||||||
READ_MODEL_JOURNAL_DATABASE_URL read_model_journal
|
READ_MODEL_JOURNAL_DATABASE_URL read_model_journal
|
||||||
RESERVED_TURN_DATABASE_URL core
|
RESERVED_TURN_DATABASE_URL core
|
||||||
SECURITY_TRANSPORT_DATABASE_URL security_transport
|
SECURITY_TRANSPORT_DATABASE_URL security_transport
|
||||||
SELECT_POOL_DATABASE_URL select_pool
|
SELECT_POOL_DATABASE_URL select_pool
|
||||||
|
SCENARIO_LIFECYCLE_DATABASE_URL scenario_lifecycle
|
||||||
TURN_DAEMON_LEASE_DATABASE_URL core
|
TURN_DAEMON_LEASE_DATABASE_URL core
|
||||||
TURN_COMMAND_DURABLE_MATRIX_DATABASE_URL reference_command_durable_matrix
|
TURN_COMMAND_DURABLE_MATRIX_DATABASE_URL reference_command_durable_matrix
|
||||||
TURN_DIFFERENTIAL_DATABASE_URL core
|
TURN_DIFFERENTIAL_DATABASE_URL core
|
||||||
|
|||||||
|
@@ -14,7 +14,7 @@ import {
|
|||||||
type RedisConnector,
|
type RedisConnector,
|
||||||
} from '@sammo-ts/infra';
|
} from '@sammo-ts/infra';
|
||||||
|
|
||||||
const databaseUrl = process.env.INPUT_EVENT_DATABASE_URL;
|
const databaseUrl = process.env.SCENARIO_LIFECYCLE_DATABASE_URL;
|
||||||
const integration = describe.skipIf(!databaseUrl);
|
const integration = describe.skipIf(!databaseUrl);
|
||||||
const scenarioId = 2900;
|
const scenarioId = 2900;
|
||||||
const userIds = ['scenario29-betting-winner', 'scenario29-betting-loser'] as const;
|
const userIds = ['scenario29-betting-winner', 'scenario29-betting-loser'] as const;
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ node_tag=$(printf '%s' "${CI_NODE_INDEX:-local}" | tr -cd 'a-zA-Z0-9_' | tr 'A-Z
|
|||||||
run_id=$(date -u +%m%d%H%M%S)_$$_${node_tag}
|
run_id=$(date -u +%m%d%H%M%S)_$$_${node_tag}
|
||||||
export CONDITIONAL_INTEGRATION_RUN_ID=$run_id
|
export CONDITIONAL_INTEGRATION_RUN_ID=$run_id
|
||||||
schema_ownership_token="sammo-conditional-integration:$run_id"
|
schema_ownership_token="sammo-conditional-integration:$run_id"
|
||||||
supported_registry_modes="core create_general external_fixture gateway_runtime immediate_action npc_possession read_model_journal reference_command_durable_matrix reference_full_lifecycle reference_live_sortie reference_npc_possession security_transport select_pool web_push_gateway"
|
supported_registry_modes="core create_general external_fixture gateway_runtime immediate_action npc_possession read_model_journal reference_command_durable_matrix reference_full_lifecycle reference_live_sortie reference_npc_possession scenario_lifecycle security_transport select_pool web_push_gateway"
|
||||||
term_grace_seconds=${CONDITIONAL_INTEGRATION_TERM_GRACE_SECONDS:-10}
|
term_grace_seconds=${CONDITIONAL_INTEGRATION_TERM_GRACE_SECONDS:-10}
|
||||||
case "$term_grace_seconds" in
|
case "$term_grace_seconds" in
|
||||||
''|*[!0-9]*)
|
''|*[!0-9]*)
|
||||||
@@ -55,6 +55,7 @@ if [ "$term_grace_seconds" -lt 1 ] || [ "$term_grace_seconds" -gt 60 ]; then
|
|||||||
fi
|
fi
|
||||||
integration_schema=${CONDITIONAL_INTEGRATION_SCHEMA:-ci_${run_id}_integration}
|
integration_schema=${CONDITIONAL_INTEGRATION_SCHEMA:-ci_${run_id}_integration}
|
||||||
scenario_schema=${SCENARIO_SEED_INTEGRATION_SCHEMA:-ci_${run_id}_scenario_seed}
|
scenario_schema=${SCENARIO_SEED_INTEGRATION_SCHEMA:-ci_${run_id}_scenario_seed}
|
||||||
|
scenario_lifecycle_schema=${SCENARIO_LIFECYCLE_INTEGRATION_SCHEMA:-ci_${run_id}_scenario_lifecycle}
|
||||||
npc_possession_schema=${NPC_POSSESSION_INTEGRATION_SCHEMA:-ci_${run_id}_npc_possession_integration}
|
npc_possession_schema=${NPC_POSSESSION_INTEGRATION_SCHEMA:-ci_${run_id}_npc_possession_integration}
|
||||||
create_general_schema=${CREATE_GENERAL_INTEGRATION_SCHEMA:-ci_${run_id}_create_general_integration}
|
create_general_schema=${CREATE_GENERAL_INTEGRATION_SCHEMA:-ci_${run_id}_create_general_integration}
|
||||||
select_pool_schema=${SELECT_POOL_INTEGRATION_SCHEMA:-ci_${run_id}_select_pool_integration}
|
select_pool_schema=${SELECT_POOL_INTEGRATION_SCHEMA:-ci_${run_id}_select_pool_integration}
|
||||||
@@ -71,6 +72,7 @@ turn_full_lifecycle_schema=${TURN_FULL_LIFECYCLE_PERSISTENCE_SCHEMA:-ci_${run_id
|
|||||||
for schema in \
|
for schema in \
|
||||||
"$integration_schema" \
|
"$integration_schema" \
|
||||||
"$scenario_schema" \
|
"$scenario_schema" \
|
||||||
|
"$scenario_lifecycle_schema" \
|
||||||
"$npc_possession_schema" \
|
"$npc_possession_schema" \
|
||||||
"$create_general_schema" \
|
"$create_general_schema" \
|
||||||
"$select_pool_schema" \
|
"$select_pool_schema" \
|
||||||
@@ -485,6 +487,7 @@ cleanup_resources_started=1
|
|||||||
create_owned_schema "$integration_schema"
|
create_owned_schema "$integration_schema"
|
||||||
create_owned_schema "$npc_possession_schema"
|
create_owned_schema "$npc_possession_schema"
|
||||||
create_owned_schema "$scenario_schema"
|
create_owned_schema "$scenario_schema"
|
||||||
|
create_owned_schema "$scenario_lifecycle_schema"
|
||||||
|
|
||||||
database_url=$(build_database_url "$integration_schema")
|
database_url=$(build_database_url "$integration_schema")
|
||||||
export POSTGRES_SCHEMA=$integration_schema
|
export POSTGRES_SCHEMA=$integration_schema
|
||||||
@@ -510,6 +513,17 @@ run_marked_tests app/game-api "$core_database_markers" "game_api_postgresql"
|
|||||||
run_marked_tests app/game-engine "$core_database_markers" "game_engine_postgresql"
|
run_marked_tests app/game-engine "$core_database_markers" "game_engine_postgresql"
|
||||||
run_marked_tests tools/integration-tests "$core_database_markers" "snapshot_postgresql"
|
run_marked_tests tools/integration-tests "$core_database_markers" "snapshot_postgresql"
|
||||||
|
|
||||||
|
scenario_lifecycle_database_url=$(build_database_url "$scenario_lifecycle_schema")
|
||||||
|
(
|
||||||
|
export POSTGRES_SCHEMA=$scenario_lifecycle_schema
|
||||||
|
export DATABASE_URL=$scenario_lifecycle_database_url
|
||||||
|
pnpm --filter @sammo-ts/infra prisma:migrate:deploy:game
|
||||||
|
)
|
||||||
|
export SCENARIO_LIFECYCLE_DATABASE_URL=$scenario_lifecycle_database_url
|
||||||
|
run_marked_tests tools/integration-tests \
|
||||||
|
"$(markers_for_mode scenario_lifecycle)" \
|
||||||
|
"scenario_lifecycle_postgresql"
|
||||||
|
|
||||||
create_owned_schema "$read_model_journal_schema"
|
create_owned_schema "$read_model_journal_schema"
|
||||||
read_model_journal_database_url=$(build_database_url "$read_model_journal_schema")
|
read_model_journal_database_url=$(build_database_url "$read_model_journal_schema")
|
||||||
(
|
(
|
||||||
|
|||||||
Reference in New Issue
Block a user