Migrate remaining legacy operator workflows
This commit is contained in:
@@ -16,6 +16,10 @@ const playoutBridge = fs.readFileSync(
|
||||
const operatorGate = fs.readFileSync(
|
||||
path.join(repositoryRoot, "src", "MBN_STOCK_WEBVIEW.Core", "Playout", "OperatorMutationGate.cs"),
|
||||
"utf8");
|
||||
const stockMasterValidation = fs.readFileSync(
|
||||
path.join(repositoryRoot, "src", "MBN_STOCK_WEBVIEW.Core", "Data",
|
||||
"StockMasterIdentityValidation.cs"),
|
||||
"utf8");
|
||||
|
||||
const krxSelection = Object.freeze({
|
||||
market: "krx",
|
||||
@@ -89,6 +93,11 @@ test("bridge contract pins every independent request and event name", () => {
|
||||
mutation: "operator-catalog-mutation-results",
|
||||
error: "operator-catalog-error"
|
||||
});
|
||||
assert.deepEqual(workflow.stockMasterContract, {
|
||||
request: "search-stocks",
|
||||
result: "stock-search-results",
|
||||
error: "stock-search-error"
|
||||
});
|
||||
assert.deepEqual(workflow.limits, {
|
||||
maximumThemeItems: 240,
|
||||
maximumRecommendations: 100,
|
||||
@@ -97,6 +106,43 @@ test("bridge contract pins every independent request and event name", () => {
|
||||
});
|
||||
});
|
||||
|
||||
test("stock master search accepts only complete unique typed identities", () => {
|
||||
const request = workflow.createStockMasterSearchRequest("stock-master-1", " 삼성 ", 25);
|
||||
assert.deepEqual(request, {
|
||||
requestId: "stock-master-1",
|
||||
query: "삼성",
|
||||
maximumResults: 25
|
||||
});
|
||||
const base = {
|
||||
requestId: request.requestId,
|
||||
query: request.query,
|
||||
retrievedAt: "2026-07-12T10:00:00+09:00",
|
||||
totalRowCount: 1,
|
||||
truncated: false,
|
||||
results: [{
|
||||
market: "kospi", source: "oracle", name: "삼성전자", code: "005930"
|
||||
}]
|
||||
};
|
||||
assert.ok(workflow.normalizeStockMasterSearchResult(base, request));
|
||||
assert.equal(workflow.normalizeStockMasterSearchResult({ ...base, truncated: true }, request), null);
|
||||
assert.equal(workflow.normalizeStockMasterSearchResult({
|
||||
...base,
|
||||
results: [{ ...base.results[0], extra: true }]
|
||||
}, request), null);
|
||||
assert.equal(workflow.normalizeStockMasterSearchResult({
|
||||
...base,
|
||||
totalRowCount: 2,
|
||||
results: [
|
||||
base.results[0],
|
||||
{ market: "kosdaq", source: "oracle", name: "삼성전자", code: "123456" }
|
||||
]
|
||||
}, request), null);
|
||||
assert.equal(workflow.normalizeStockMasterSearchResult({
|
||||
...base,
|
||||
requestId: "stale"
|
||||
}, request), null);
|
||||
});
|
||||
|
||||
test("native partial pins the same contract and fail-closed write gates without a retry loop", () => {
|
||||
for (const requestType of Object.values(workflow.bridgeContract.requests)) {
|
||||
assert.match(nativeBridge, new RegExp(`"${requestType}"`));
|
||||
@@ -120,6 +166,13 @@ test("native partial pins the same contract and fail-closed write gates without
|
||||
assert.match(nativeBridge, /LatchOperatorCatalogWriteQuarantine/);
|
||||
assert.match(nativeBridge, /OUTCOME_UNKNOWN/);
|
||||
assert.match(nativeBridge, /OperatorCatalogMutationExecutor/);
|
||||
assert.match(nativeBridge, /LegacyStockMasterIdentityValidationService/);
|
||||
assert.match(nativeBridge,
|
||||
/await validationBody\(requestToken\)[\s\S]*mutationDispatched = true;/s);
|
||||
assert.match(stockMasterValidation, /STOCK_MASTER_VERIFY_KOSPI/);
|
||||
assert.match(stockMasterValidation, /STOCK_MASTER_VERIFY_KOSDAQ/);
|
||||
assert.match(stockMasterValidation, /STOCK_MASTER_VERIFY_NXT_KOSPI/);
|
||||
assert.match(stockMasterValidation, /STOCK_MASTER_VERIFY_NXT_KOSDAQ/);
|
||||
assert.doesNotMatch(nativeBridge, /MaximumRetryCount|Task\.Delay|while\s*\(/);
|
||||
});
|
||||
|
||||
@@ -247,6 +300,23 @@ test("new theme drafts require a valid title before create and delete keeps full
|
||||
});
|
||||
});
|
||||
|
||||
test("existing theme edits preserve legacy three through eight digit codes", () => {
|
||||
const edit = workflow.createThemeEditDraft(
|
||||
{ ...krxSelection, themeCode: "007" },
|
||||
{
|
||||
...themePreview(),
|
||||
selection: { ...themePreview().selection, themeCode: "007" }
|
||||
});
|
||||
edit.draft.themeTitle = "수정 테마";
|
||||
assert.equal(
|
||||
workflow.createThemeReplaceRequest("legacy-theme-replace", edit)
|
||||
.expectedIdentity.themeCode,
|
||||
"007");
|
||||
assert.throws(() => workflow.createThemeCreateRequest(
|
||||
"legacy-theme-create",
|
||||
edit.draft), /eight-digit/i);
|
||||
});
|
||||
|
||||
test("expert selection plus preview becomes editable and preserves buy amounts while reindexing", () => {
|
||||
const edit = workflow.createExpertEditDraft(expertSelection, expertPreview());
|
||||
assert.deepEqual(edit, {
|
||||
|
||||
Reference in New Issue
Block a user