README + add tests cases
This commit is contained in:
@@ -2,10 +2,11 @@
|
||||
|
||||
This folder keeps the skill tests intentionally small and executable.
|
||||
|
||||
The suite covers only two themes:
|
||||
The suite covers three offline themes:
|
||||
|
||||
1. `functions.test.mjs` checks local functions and output contracts.
|
||||
2. `sales-prompts.test.mjs` checks whether common sales prompts map to quick, documented CLI commands for Oracle DV.
|
||||
2. `lib-contracts.test.mjs` checks shared CLI, auth, runtime-path, and output helpers.
|
||||
3. `sales-prompts.test.mjs` checks whether common sales prompts map to quick, documented CLI commands for Oracle DV, and verifies high-risk render-only CLI contracts such as mode conflicts, scope aliases, and query source inference.
|
||||
|
||||
The tests read `SKILL.md` and `references/*.md` as the source of truth. They should not keep a parallel copy of long parameter lists, aliases, defaults, or week mappings.
|
||||
|
||||
@@ -25,6 +26,10 @@ node tests/run.mjs
|
||||
|
||||
Offline mode does not call Oracle DV. Live mode starts with a no-cache session-context read, then runs prompt-sized backend checks. Prompts that should be org-wide commercial reads assert the primary source from `references/api-map.md`, reject fallback to `DV - SE Team`, and use larger live limits when row volume is part of the expected behavior.
|
||||
|
||||
Offline coverage should favor stable behavior over exhaustive combinations. The highest-value cases are date-window resolution, manager/resource normalization, scope disclosure, query source selection, output money formatting, API row metrics, and early CLI validation before any backend call.
|
||||
|
||||
The focused-intent capability matrix protects the intended small surface area: owner, owner-manager, and resource close-window asks get first-class intents, while multi-axis combinations and SR country/owner asks stay out of scope until a real use case justifies them.
|
||||
|
||||
Every test invocation of `fetch-sales-intelligence.mjs` is forced through `--no-cache`.
|
||||
|
||||
When `ORACLE_SI_RUNTIME_HOME` is set, the tests copy `auth-state.json` and `session-meta.json` into a temporary runtime first, so live checks do not rewrite the source runtime. Set `ORACLE_SI_TEST_RUNTIME_HOME` when you intentionally want to provide a writable test runtime.
|
||||
|
||||
@@ -144,6 +144,22 @@ functionTest(
|
||||
assert.equal(quarter.toDate, "2026-05-31");
|
||||
|
||||
const weekYear = parseDefaultWeekYear(catalogs.scriptCatalog);
|
||||
const firstWeek = fetchModule.resolveDateWindow({
|
||||
week: "week 1",
|
||||
"week-year": String(weekYear),
|
||||
});
|
||||
assert.equal(firstWeek.weekLabel, "W1");
|
||||
assert.equal(firstWeek.fromDate, `${weekYear}-06-01`);
|
||||
assert.equal(firstWeek.toDate, `${weekYear}-06-07`);
|
||||
|
||||
const lastWeek = fetchModule.resolveDateWindow({
|
||||
week: "w53",
|
||||
"week-year": String(weekYear),
|
||||
});
|
||||
assert.equal(lastWeek.weekLabel, "W53");
|
||||
assert.equal(lastWeek.fromDate, `${weekYear + 1}-05-31`);
|
||||
assert.equal(lastWeek.toDate, `${weekYear + 1}-05-31`);
|
||||
|
||||
const expectedWeek = parseWeekRow(catalogs.scriptCatalog, "W43");
|
||||
const week = fetchModule.resolveDateWindow({
|
||||
week: expectedWeek.weekLabel.toLowerCase(),
|
||||
@@ -157,7 +173,15 @@ functionTest(
|
||||
() => fetchModule.resolveDateWindow({ date: "2026-05-02", month: "2026-05" }),
|
||||
/Use either --date/
|
||||
);
|
||||
return `single=${exact.fromDate}; month=${month.fromDate}..${month.toDate}; quarter=${quarter.fromDate}..${quarter.toDate}; week=${week.fromDate}..${week.toDate}; invalid-combo rejected`;
|
||||
assert.throws(
|
||||
() => fetchModule.resolveDateWindow({ week: "w54", "week-year": String(weekYear) }),
|
||||
/Unsupported week token/
|
||||
);
|
||||
assert.throws(
|
||||
() => fetchModule.resolveDateWindow({ "from-date": "2026-05-10", "to-date": "2026-05-02" }),
|
||||
/from-date 2026-05-10 is after to-date 2026-05-02/
|
||||
);
|
||||
return `single=${exact.fromDate}; month=${month.fromDate}..${month.toDate}; quarter=${quarter.fromDate}..${quarter.toDate}; week=${week.fromDate}..${week.toDate}; first=${firstWeek.fromDate}; last=${lastWeek.fromDate}; invalid-combo/window rejected`;
|
||||
});
|
||||
|
||||
functionTest(
|
||||
@@ -229,7 +253,25 @@ functionTest(
|
||||
}).join("\n");
|
||||
assert.doesNotMatch(withoutCluster, /"Cluster" =/);
|
||||
|
||||
return "explicit cluster is applied; absent cluster preserves existing unfiltered geography behavior";
|
||||
const ownerClauses = fetchModule.buildPrimaryOpportunityWhereClauses({
|
||||
fromDateSql: "DATE '2026-05-01'",
|
||||
toDateSql: "DATE '2026-05-31'",
|
||||
statuses: ["Open"],
|
||||
cluster: "Brazil",
|
||||
ownerEmail: "seller.one@oracle.com",
|
||||
}).join("\n");
|
||||
assert.match(ownerClauses, /"Level 7 Territory Owner E-mail" = 'seller.one@oracle.com'/);
|
||||
|
||||
const ownerManagerClauses = fetchModule.buildPrimaryOpportunityWhereClauses({
|
||||
fromDateSql: "DATE '2026-05-01'",
|
||||
toDateSql: "DATE '2026-05-31'",
|
||||
statuses: ["Open"],
|
||||
ownerManagerEmail: "sales.manager@oracle.com",
|
||||
}).join("\n");
|
||||
assert.match(ownerManagerClauses, /"Level 2 Territory Owner E-mail" = 'sales.manager@oracle.com'/);
|
||||
assert.match(ownerManagerClauses, /"Level 6 Territory Owner E-mail" = 'sales.manager@oracle.com'/);
|
||||
|
||||
return "explicit cluster is applied; absent cluster preserves existing unfiltered geography behavior; owner filters are applied";
|
||||
});
|
||||
|
||||
functionTest(
|
||||
@@ -255,6 +297,57 @@ functionTest(
|
||||
return "manager/resource workload cluster is explicit when provided and Brazil by default";
|
||||
});
|
||||
|
||||
functionTest(
|
||||
"workload-close-window-sql",
|
||||
"workload close-window SQL supports date, cluster, owner, manager, and forecast filters",
|
||||
{
|
||||
entrada: "buildWorkloadCloseWindowSql com owner, manager e forecast",
|
||||
},
|
||||
() => {
|
||||
const sql = fetchModule.buildWorkloadCloseWindowSql({
|
||||
fromDateSql: "DATE '2026-05-01'",
|
||||
toDateSql: "DATE '2026-05-31'",
|
||||
cluster: "Mexico",
|
||||
ownerEmail: "territory.owner@oracle.com",
|
||||
ownerManagerEmail: "territory.manager@oracle.com",
|
||||
forecastTypes: ["Forecast"],
|
||||
});
|
||||
|
||||
assert.match(sql, /"Cluster" = 'Mexico'/);
|
||||
assert.match(sql, /"Revenue Line Close Date" >= DATE '2026-05-01'/);
|
||||
assert.match(sql, /"Level 7 Territory Owner E-mail" = 'territory.owner@oracle.com'/);
|
||||
assert.match(sql, /"Level 2 Territory Owner E-mail" = 'territory.manager@oracle.com'/);
|
||||
assert.match(sql, /"Revenue Line Forecast Type Group" IN \('Forecast'\)/);
|
||||
return "workload SQL includes geography, window, owner hierarchy, and forecast filters";
|
||||
});
|
||||
|
||||
functionTest(
|
||||
"manager-email-list-normalization",
|
||||
"manager email list filters normalize short ids, merge flags, and deduplicate",
|
||||
{
|
||||
entrada: "resolveManagerEmailListFilter com lista mista + --manager-email duplicado",
|
||||
},
|
||||
() => {
|
||||
const result = fetchModule.resolveManagerEmailListFilter({
|
||||
"manager-email-list": " Resource.Manager.B , resource.manager.a@oracle.com, RESOURCE.MANAGER.B@ORACLE.COM ",
|
||||
"manager-email": "resource.manager.a",
|
||||
});
|
||||
|
||||
assert.deepEqual(result.managerEmails, [
|
||||
"resource.manager.a@oracle.com",
|
||||
"resource.manager.b@oracle.com",
|
||||
]);
|
||||
assert.equal(
|
||||
result.managerEmailsSqlList,
|
||||
"'resource.manager.a@oracle.com', 'resource.manager.b@oracle.com'"
|
||||
);
|
||||
assert.throws(
|
||||
() => fetchModule.resolveManagerEmailListFilter({ "manager-email-list": " , " }),
|
||||
/Pass --manager-email-list or --manager-email/
|
||||
);
|
||||
return `managerEmails=${result.managerEmails.join(",")}`;
|
||||
});
|
||||
|
||||
functionTest(
|
||||
"coverage-limit-cap",
|
||||
"coverage enrichment limit never exceeds Oracle DV maximum",
|
||||
@@ -297,7 +390,24 @@ functionTest(
|
||||
assert.equal(explicitMonth.month, "2026-04");
|
||||
assert.equal(explicitMonth.quarter, undefined);
|
||||
assert.equal(explicitMonth.solution, expectedDefaults.solution);
|
||||
return `quarter=${defaults.quarter}; solution=${defaults.solution}; revenueTypeGroup=${defaults["revenue-type-group"]}; explicitMonth preserved`;
|
||||
|
||||
const ownerWindowDefaults = fetchModule.applyCloseWindowIntentDefaults({
|
||||
intent: "workloads-by-owner-close-window",
|
||||
"owner-email": "territory.owner@oracle.com",
|
||||
});
|
||||
assert.equal(ownerWindowDefaults["from-date"], "today");
|
||||
assert.equal(ownerWindowDefaults["to-date"], "+7d");
|
||||
assert.equal(ownerWindowDefaults.quarter, undefined);
|
||||
|
||||
const explicitWeek = fetchModule.applyCloseWindowIntentDefaults({
|
||||
intent: "workloads-by-owner-close-window",
|
||||
"owner-email": "territory.owner@oracle.com",
|
||||
week: "w43",
|
||||
});
|
||||
assert.equal(explicitWeek.week, "w43");
|
||||
assert.equal(explicitWeek["from-date"], undefined);
|
||||
assert.equal(explicitWeek["to-date"], undefined);
|
||||
return `quarter=${defaults.quarter}; solution=${defaults.solution}; revenueTypeGroup=${defaults["revenue-type-group"]}; explicitMonth preserved; closeWindowDefaults=${ownerWindowDefaults["from-date"]}..${ownerWindowDefaults["to-date"]}`;
|
||||
});
|
||||
|
||||
functionTest(
|
||||
@@ -373,6 +483,44 @@ functionTest(
|
||||
return "org-wide sem SEC_ARIA; my-team com SEC_ARIA; SR team usa levels 1..4; lead só quando explícito";
|
||||
});
|
||||
|
||||
functionTest(
|
||||
"scope-components-contract",
|
||||
"scope descriptors expose component-level source and scope details",
|
||||
{
|
||||
entrada: "buildIntentScopeDescriptor para mixed current-team e workload manager/resource",
|
||||
},
|
||||
() => {
|
||||
const mixed = fetchModule.buildIntentScopeDescriptor(
|
||||
"opportunities-current-team-close-window",
|
||||
{ view: "opportunities-close-window" }
|
||||
);
|
||||
assert.equal(mixed.scopeMode, "mixed-consistent");
|
||||
assert.equal(mixed.preserveUnmatchedRows, true);
|
||||
assert.deepEqual(
|
||||
mixed.components.map((component) => [component.name, component.scope]),
|
||||
[
|
||||
["commercialRows", "org-wide"],
|
||||
["resourceCoverage", "my-team-only"],
|
||||
]
|
||||
);
|
||||
assert.match(mixed.explanation, /Rows without a resource match stay/);
|
||||
|
||||
const orgWide = fetchModule.buildIntentScopeDescriptor(
|
||||
"workloads-by-manager-resource-close-window",
|
||||
{ view: "opportunities-close-window" }
|
||||
);
|
||||
assert.equal(orgWide.scopeMode, "org-wide");
|
||||
assert.deepEqual(
|
||||
orgWide.components.map((component) => [component.name, component.scope]),
|
||||
[
|
||||
["commercialRows", "org-wide"],
|
||||
["managerResourceAllocation", "org-wide"],
|
||||
]
|
||||
);
|
||||
assert.match(orgWide.explanation, /Manager\/resource allocation: org-wide/);
|
||||
return `mixed=${mixed.components.length}; orgWide=${orgWide.components.length}`;
|
||||
});
|
||||
|
||||
functionTest(
|
||||
"current-team-summary",
|
||||
"current-team coverage summaries preserve unmatched commercial rows",
|
||||
|
||||
@@ -83,6 +83,41 @@
|
||||
"catalogRefs": ["SKILL.md"],
|
||||
"args": ["--intent", "opportunities-current-team-close-window", "--from-date", "today", "--to-date", "+4d", "--limit", "10"]
|
||||
},
|
||||
{
|
||||
"id": "owner-opportunities-week",
|
||||
"prompt": "Quais oportunidades do vendedor {{territoryOwnerEmail}} fecham na week 43?",
|
||||
"catalogRefs": ["SKILL.md", "references/script-catalog.md#week-reference"],
|
||||
"args": ["--intent", "opportunities-by-owner-close-window", "--owner-email", "{{territoryOwnerEmail}}", "--week", "w43", "--week-year", "catalog-default", "--limit", "10"],
|
||||
"live": false
|
||||
},
|
||||
{
|
||||
"id": "owner-manager-opportunities-month",
|
||||
"prompt": "Quais oportunidades dos owners abaixo de {{territoryManagerEmail}} fecham em maio de 2026?",
|
||||
"catalogRefs": ["SKILL.md", "references/initial-context-catalog.md"],
|
||||
"args": ["--intent", "opportunities-by-owner-manager-close-window", "--owner-manager-email", "{{territoryManagerEmail}}", "--month", "2026-05", "--limit", "10"],
|
||||
"live": false
|
||||
},
|
||||
{
|
||||
"id": "resource-workloads-week",
|
||||
"prompt": "Quais workloads do recurso {{resourceMember}} fecham na week 43?",
|
||||
"catalogRefs": ["SKILL.md", "references/script-catalog.md#week-reference"],
|
||||
"args": ["--intent", "workloads-by-resource-close-window", "--resource", "{{resourceMember}}", "--week", "w43", "--week-year", "catalog-default", "--limit", "10"],
|
||||
"live": false
|
||||
},
|
||||
{
|
||||
"id": "owner-workloads-month",
|
||||
"prompt": "Quais workloads do vendedor {{territoryOwnerEmail}} fecham em maio de 2026?",
|
||||
"catalogRefs": ["SKILL.md"],
|
||||
"args": ["--intent", "workloads-by-owner-close-window", "--owner-email", "{{territoryOwnerEmail}}", "--month", "2026-05", "--limit", "10"],
|
||||
"live": false
|
||||
},
|
||||
{
|
||||
"id": "owner-manager-workloads-week",
|
||||
"prompt": "Quais workloads dos owners abaixo de {{territoryManagerEmail}} fecham na week 43?",
|
||||
"catalogRefs": ["SKILL.md", "references/initial-context-catalog.md", "references/script-catalog.md#week-reference"],
|
||||
"args": ["--intent", "workloads-by-owner-manager-close-window", "--owner-manager-email", "{{territoryManagerEmail}}", "--week", "w43", "--week-year", "catalog-default", "--limit", "10"],
|
||||
"live": false
|
||||
},
|
||||
{
|
||||
"id": "ad-hoc-query",
|
||||
"prompt": "Quero opportunityId, cliente, close date, booking e workload das oportunidades de 2026-05-02.",
|
||||
|
||||
@@ -67,6 +67,29 @@ function globalParameterFlags(parameters) {
|
||||
return new Set((parameters || []).map((parameter) => parameter.cli));
|
||||
}
|
||||
|
||||
const expectedFocusedIntentCapabilities = {
|
||||
"opportunities-by-owner-close-window": {
|
||||
required: ["--owner-email"],
|
||||
optional: ["--date", "--month", "--quarter", "--week", "--week-year", "--from-date", "--to-date", "--cluster", "--opty-status", "--limit"],
|
||||
},
|
||||
"opportunities-by-owner-manager-close-window": {
|
||||
required: ["--owner-manager-email"],
|
||||
optional: ["--date", "--month", "--quarter", "--week", "--week-year", "--from-date", "--to-date", "--cluster", "--opty-status", "--limit"],
|
||||
},
|
||||
"workloads-by-resource-close-window": {
|
||||
required: ["--resource"],
|
||||
optional: ["--date", "--month", "--quarter", "--week", "--week-year", "--from-date", "--to-date", "--cluster", "--forecast-type", "--limit"],
|
||||
},
|
||||
"workloads-by-owner-close-window": {
|
||||
required: ["--owner-email"],
|
||||
optional: ["--date", "--month", "--quarter", "--week", "--week-year", "--from-date", "--to-date", "--cluster", "--forecast-type", "--limit"],
|
||||
},
|
||||
"workloads-by-owner-manager-close-window": {
|
||||
required: ["--owner-manager-email"],
|
||||
optional: ["--date", "--month", "--quarter", "--week", "--week-year", "--from-date", "--to-date", "--cluster", "--forecast-type", "--limit"],
|
||||
},
|
||||
};
|
||||
|
||||
test("sales prompt commands are short, direct, and documented in references", () => {
|
||||
const flagsFromReferences = documentedFlags(catalogs);
|
||||
|
||||
@@ -170,6 +193,32 @@ test("intent prompt commands match the live CLI contract without local flag copi
|
||||
}
|
||||
});
|
||||
|
||||
test("focused owner/resource close-window intents expose the expected offline contract", async () => {
|
||||
for (const [intentName, expected] of Object.entries(expectedFocusedIntentCapabilities)) {
|
||||
const result = await runFetch(["--describe-intent", intentName]);
|
||||
const parameters = result.json.intent.parameters || [];
|
||||
const flags = new Set(parameters.map((parameter) => parameter.cli));
|
||||
const requiredFlags = new Set(
|
||||
parameters.filter((parameter) => parameter.required).map((parameter) => parameter.cli)
|
||||
);
|
||||
|
||||
assert.equal(result.json.intent.name, intentName);
|
||||
assert.ok(catalogMentionsIntent(catalogs, intentName), `${intentName} must be documented.`);
|
||||
|
||||
for (const flag of expected.required) {
|
||||
assert.ok(flags.has(flag), `${intentName} is missing ${flag}`);
|
||||
assert.ok(requiredFlags.has(flag), `${intentName} must require ${flag}`);
|
||||
}
|
||||
|
||||
for (const flag of expected.optional) {
|
||||
assert.ok(flags.has(flag), `${intentName} is missing optional ${flag}`);
|
||||
assert.equal(requiredFlags.has(flag), false, `${intentName} should not require optional ${flag}`);
|
||||
}
|
||||
|
||||
assert.equal(flags.has("--country"), false, `${intentName} should use --cluster, not a new --country flag.`);
|
||||
}
|
||||
});
|
||||
|
||||
test("ad hoc sales prompt renders an org-wide opportunity query", async () => {
|
||||
const item = promptCases.find((entry) => entry.id === "ad-hoc-query");
|
||||
const args = caseArgs(item);
|
||||
@@ -208,6 +257,79 @@ test("org-wide query money aliases use the pipeline measure that Oracle DV accep
|
||||
assert.match(result.json.expandedSql, /"Columns"\."Revenue Line Close Date" >= DATE '2026-04-26'/);
|
||||
});
|
||||
|
||||
test("query mode keeps commercial auto-scope and SR auto-scope separated", async () => {
|
||||
const commercial = await runFetch([
|
||||
"--query",
|
||||
"--select",
|
||||
"opportunityId,customerName",
|
||||
"--where",
|
||||
"cluster = 'Brazil'",
|
||||
"--render-sql",
|
||||
]);
|
||||
assert.equal(commercial.json.query.source, parsePrimaryCommercialSourceName(catalogs.apiMap));
|
||||
assert.equal(commercial.json.scope.scopeMode, "org-wide");
|
||||
assert.doesNotMatch(commercial.json.expandedSql, /SEC_ARIA_SI-SR-TEAM/);
|
||||
|
||||
const sr = await runFetch([
|
||||
"--query",
|
||||
"--select",
|
||||
"srNumber,customerName",
|
||||
"--render-sql",
|
||||
]);
|
||||
assert.equal(sr.json.query.source, "DV - SE Team");
|
||||
assert.equal(sr.json.scope.scopeMode, "my-team-only");
|
||||
assert.match(sr.json.expandedSql, /SEC_ARIA_SI-SR-TEAM/);
|
||||
});
|
||||
|
||||
test("query mode honors my-team scope aliases for otherwise org-wide commercial fields", async () => {
|
||||
for (const scopeAlias of ["team", "current-team"]) {
|
||||
const result = await runFetch([
|
||||
"--query",
|
||||
"--select",
|
||||
"opportunityId,customerName",
|
||||
"--scope",
|
||||
scopeAlias,
|
||||
"--render-sql",
|
||||
]);
|
||||
|
||||
assert.equal(result.json.query.requestedScope, "my-team-only", scopeAlias);
|
||||
assert.equal(result.json.query.effectiveScope, "my-team-only", scopeAlias);
|
||||
assert.equal(result.json.query.source, "DV - SE Team", scopeAlias);
|
||||
assert.match(result.json.expandedSql, /SEC_ARIA_SI-SR-TEAM/, scopeAlias);
|
||||
}
|
||||
});
|
||||
|
||||
test("CLI rejects conflicting execution modes and unsupported scopes before backend execution", async () => {
|
||||
const cases = [
|
||||
{
|
||||
args: ["--query", "--select", "opportunityId", "--sql", "select 1", "--render-sql"],
|
||||
message: /Use either --query or --sql, not both/,
|
||||
},
|
||||
{
|
||||
args: ["--query", "--select", "opportunityId", "--intent", "opportunities-default", "--render-sql"],
|
||||
message: /Use either --query or --intent, not both/,
|
||||
},
|
||||
{
|
||||
args: ["--query", "--select", "opportunityId", "--view", "opportunity-details", "--render-sql"],
|
||||
message: /Use either --query or --view, not both/,
|
||||
},
|
||||
{
|
||||
args: ["--query", "--select", "opportunityId", "--scope", "nope", "--render-sql"],
|
||||
message: /Unsupported --scope "nope"/,
|
||||
},
|
||||
{
|
||||
args: ["--render-sql"],
|
||||
message: /Pass --sql or --query together with --render-sql/,
|
||||
},
|
||||
];
|
||||
|
||||
for (const item of cases) {
|
||||
const result = await runFetch(item.args, { expectCode: 1 });
|
||||
assert.match(result.stderr, item.message);
|
||||
assert.equal(result.stdout, "");
|
||||
}
|
||||
});
|
||||
|
||||
test("CLI rejects invalid limit values before backend execution", async () => {
|
||||
for (const value of ["abc", "0", "-1", "1.5"]) {
|
||||
const result = await runFetch(
|
||||
|
||||
Reference in New Issue
Block a user