@aigne/doc-smith 0.8.11-beta → 0.8.11-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.aigne/doc-smith/config.yaml +2 -0
- package/.aigne/doc-smith/output/structure-plan.json +3 -3
- package/.aigne/doc-smith/upload-cache.yaml +252 -0
- package/.github/workflows/publish-docs.yml +67 -0
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +22 -0
- package/README.md +45 -115
- package/agents/clear/choose-contents.mjs +170 -0
- package/agents/clear/clear-auth-tokens.mjs +111 -0
- package/agents/clear/clear-document-config.mjs +39 -0
- package/agents/clear/clear-document-structure.mjs +106 -0
- package/agents/clear/clear-generated-docs.mjs +51 -0
- package/agents/clear/index.yaml +23 -0
- package/agents/evaluate/code-snippet.mjs +93 -0
- package/agents/evaluate/document-structure.yaml +70 -0
- package/agents/evaluate/document.yaml +79 -0
- package/agents/evaluate/generate-report.mjs +78 -0
- package/agents/evaluate/index.yaml +39 -0
- package/agents/generate/document-structure-tools/add-document.mjs +56 -0
- package/agents/generate/document-structure-tools/delete-document.mjs +49 -0
- package/agents/generate/document-structure-tools/move-document.mjs +82 -0
- package/agents/generate/document-structure-tools/update-document.mjs +50 -0
- package/agents/generate/generate-structure.yaml +1 -1
- package/agents/generate/update-document-structure.yaml +42 -0
- package/agents/generate/user-review-document-structure.mjs +6 -4
- package/agents/init/index.mjs +1 -1
- package/agents/publish/publish-docs.mjs +12 -3
- package/agents/translate/choose-language.mjs +1 -1
- package/agents/update/batch-update-document.yaml +7 -0
- package/agents/update/check-update-is-single.mjs +38 -0
- package/agents/update/document-tools/update-document-content.mjs +293 -0
- package/agents/update/index.yaml +4 -10
- package/agents/update/update-document-detail.yaml +52 -0
- package/agents/update/update-single-document.yaml +15 -0
- package/agents/update/user-review-document.mjs +248 -0
- package/agents/utils/choose-docs.mjs +4 -2
- package/agents/utils/format-document-structure.mjs +12 -2
- package/agents/utils/load-document-all-content.mjs +84 -0
- package/agents/utils/load-sources.mjs +4 -1
- package/aigne.yaml +59 -20
- package/assets/report-template/report.html +198 -0
- package/biome.json +14 -2
- package/docs/advanced-how-it-works.ja.md +101 -0
- package/docs/advanced-how-it-works.zh-TW.md +101 -0
- package/docs/advanced-how-it-works.zh.md +20 -20
- package/docs/advanced-quality-assurance.ja.md +96 -0
- package/docs/advanced-quality-assurance.zh-TW.md +96 -0
- package/docs/advanced-quality-assurance.zh.md +18 -18
- package/docs/advanced.ja.md +16 -0
- package/docs/advanced.zh-TW.md +16 -0
- package/docs/advanced.zh.md +4 -4
- package/docs/changelog.ja.md +309 -0
- package/docs/changelog.zh-TW.md +309 -0
- package/docs/changelog.zh.md +23 -23
- package/docs/cli-reference.ja.md +210 -0
- package/docs/cli-reference.zh-TW.md +210 -0
- package/docs/cli-reference.zh.md +21 -21
- package/docs/configuration-interactive-setup.ja.md +135 -0
- package/docs/configuration-interactive-setup.zh-TW.md +135 -0
- package/docs/configuration-interactive-setup.zh.md +29 -29
- package/docs/configuration-language-support.ja.md +94 -0
- package/docs/configuration-language-support.zh-TW.md +94 -0
- package/docs/configuration-language-support.zh.md +13 -13
- package/docs/configuration-llm-setup.ja.md +54 -0
- package/docs/configuration-llm-setup.zh-TW.md +54 -0
- package/docs/configuration-llm-setup.zh.md +12 -12
- package/docs/configuration-preferences.ja.md +129 -0
- package/docs/configuration-preferences.zh-TW.md +129 -0
- package/docs/configuration-preferences.zh.md +36 -36
- package/docs/configuration.ja.md +172 -0
- package/docs/configuration.zh-TW.md +172 -0
- package/docs/configuration.zh.md +49 -49
- package/docs/features-generate-documentation.ja.md +101 -0
- package/docs/features-generate-documentation.zh-TW.md +101 -0
- package/docs/features-generate-documentation.zh.md +17 -17
- package/docs/features-publish-your-docs.ja.md +107 -0
- package/docs/features-publish-your-docs.zh-TW.md +107 -0
- package/docs/features-publish-your-docs.zh.md +22 -22
- package/docs/features-translate-documentation.ja.md +79 -0
- package/docs/features-translate-documentation.zh-TW.md +79 -0
- package/docs/features-translate-documentation.zh.md +12 -12
- package/docs/features-update-and-refine.ja.md +138 -0
- package/docs/features-update-and-refine.zh-TW.md +138 -0
- package/docs/features-update-and-refine.zh.md +21 -21
- package/docs/features.ja.md +52 -0
- package/docs/features.zh-TW.md +52 -0
- package/docs/features.zh.md +8 -8
- package/docs/getting-started.ja.md +123 -0
- package/docs/getting-started.zh-TW.md +123 -0
- package/docs/getting-started.zh.md +24 -24
- package/docs/overview.ja.md +30 -0
- package/docs/overview.zh-TW.md +30 -0
- package/docs/overview.zh.md +8 -8
- package/package.json +19 -11
- package/prompts/common/document/content-rules-core.md +19 -0
- package/prompts/common/document/media-handling-rules.md +9 -0
- package/prompts/common/document/role-and-personality.md +15 -0
- package/prompts/common/document/user-preferences.md +9 -0
- package/prompts/common/document-structure/conflict-resolution-guidance.md +16 -0
- package/prompts/common/document-structure/document-structure-rules.md +45 -0
- package/prompts/common/document-structure/glossary.md +7 -0
- package/prompts/common/document-structure/intj-traits.md +5 -0
- package/prompts/common/document-structure/output-constraints.md +9 -0
- package/prompts/common/document-structure/user-locale-rules.md +10 -0
- package/prompts/common/document-structure/user-preferences.md +9 -0
- package/prompts/detail/custom/custom-components.md +9 -1
- package/prompts/detail/document-rules.md +6 -6
- package/prompts/detail/generate-document.md +5 -45
- package/prompts/detail/update-document.md +145 -0
- package/prompts/evaluate/document-structure.md +94 -0
- package/prompts/evaluate/document.md +149 -0
- package/prompts/structure/document-rules.md +1 -1
- package/prompts/structure/generate-structure-system.md +74 -0
- package/prompts/structure/generate-structure-user.md +41 -0
- package/prompts/structure/update-document-structure.md +118 -0
- package/prompts/translate/translate-document.md +1 -1
- package/prompts/utils/feedback-refiner.md +3 -3
- package/release-please-config.json +1 -7
- package/tests/agents/clear/choose-contents.test.mjs +280 -0
- package/tests/agents/clear/clear-auth-tokens.test.mjs +268 -0
- package/tests/agents/clear/clear-document-config.test.mjs +167 -0
- package/tests/agents/clear/clear-document-structure.test.mjs +374 -0
- package/tests/agents/clear/clear-generated-docs.test.mjs +222 -0
- package/tests/agents/evaluate/code-snippet.test.mjs +163 -0
- package/tests/agents/evaluate/fixtures/api-services.md +87 -0
- package/tests/agents/evaluate/fixtures/js-sdk.md +94 -0
- package/tests/agents/evaluate/generate-report.test.mjs +312 -0
- package/tests/agents/generate/check-document-structure.test.mjs +0 -6
- package/tests/agents/generate/document-structure-tools/add-document.test.mjs +449 -0
- package/tests/agents/generate/document-structure-tools/delete-document.test.mjs +410 -0
- package/tests/agents/generate/document-structure-tools/move-document.test.mjs +476 -0
- package/tests/agents/generate/document-structure-tools/update-document.test.mjs +548 -0
- package/tests/agents/generate/generate-structure.test.mjs +0 -6
- package/tests/agents/generate/user-review-document-structure.test.mjs +9 -9
- package/tests/agents/publish/publish-docs.test.mjs +2 -2
- package/tests/agents/update/check-update-is-single.test.mjs +300 -0
- package/tests/agents/update/document-tools/update-document-content.test.mjs +326 -0
- package/tests/agents/update/user-review-document.test.mjs +561 -0
- package/tests/agents/utils/format-document-structure.test.mjs +100 -0
- package/tests/utils/auth-utils.test.mjs +239 -1
- package/tests/utils/blocklet.test.mjs +9 -7
- package/tests/utils/constants.test.mjs +1 -1
- package/tests/utils/d2-utils.test.mjs +1 -1
- package/tests/utils/deploy.test.mjs +310 -366
- package/tests/utils/kroki-utils.test.mjs +2 -15
- package/tests/utils/linter/fixtures/css/keyword-error.css +1 -0
- package/tests/utils/linter/fixtures/css/missing-semicolon.css +1 -0
- package/tests/utils/linter/fixtures/css/syntax-error.css +1 -0
- package/tests/utils/linter/fixtures/css/undeclare-variable.css +1 -0
- package/tests/utils/linter/fixtures/css/unused-variable.css +2 -0
- package/tests/utils/linter/fixtures/css/valid-code.css +1 -0
- package/tests/utils/linter/fixtures/dockerfile/keyword-error.dockerfile +1 -0
- package/tests/utils/linter/fixtures/dockerfile/missing-semicolon.dockerfile +2 -0
- package/tests/utils/linter/fixtures/dockerfile/syntax-error.dockerfile +2 -0
- package/tests/utils/linter/fixtures/dockerfile/undeclare-variable.dockerfile +1 -0
- package/tests/utils/linter/fixtures/dockerfile/unused-variable.dockerfile +1 -0
- package/tests/utils/linter/fixtures/dockerfile/valid-code.dockerfile +2 -0
- package/tests/utils/linter/fixtures/go/keyword-error.go +5 -0
- package/tests/utils/linter/fixtures/go/missing-semicolon.go +5 -0
- package/tests/utils/linter/fixtures/go/syntax-error.go +6 -0
- package/tests/utils/linter/fixtures/go/undeclare-variable.go +5 -0
- package/tests/utils/linter/fixtures/go/unused-variable.go +5 -0
- package/tests/utils/linter/fixtures/go/valid-code.go +7 -0
- package/tests/utils/linter/fixtures/js/keyword-error.js +3 -0
- package/tests/utils/linter/fixtures/js/missing-semicolon.js +6 -0
- package/tests/utils/linter/fixtures/js/syntax-error.js +4 -0
- package/tests/utils/linter/fixtures/js/undeclare-variable.js +3 -0
- package/tests/utils/linter/fixtures/js/unused-variable.js +7 -0
- package/tests/utils/linter/fixtures/js/valid-code.js +15 -0
- package/tests/utils/linter/fixtures/json/keyword-error.json +1 -0
- package/tests/utils/linter/fixtures/json/missing-semicolon.json +1 -0
- package/tests/utils/linter/fixtures/json/syntax-error.json +1 -0
- package/tests/utils/linter/fixtures/json/undeclare-variable.json +1 -0
- package/tests/utils/linter/fixtures/json/unused-variable.json +1 -0
- package/tests/utils/linter/fixtures/json/valid-code.json +1 -0
- package/tests/utils/linter/fixtures/jsx/keyword-error.jsx +5 -0
- package/tests/utils/linter/fixtures/jsx/missing-semicolon.jsx +5 -0
- package/tests/utils/linter/fixtures/jsx/syntax-error.jsx +5 -0
- package/tests/utils/linter/fixtures/jsx/undeclare-variable.jsx +5 -0
- package/tests/utils/linter/fixtures/jsx/unused-variable.jsx +4 -0
- package/tests/utils/linter/fixtures/jsx/valid-code.jsx +5 -0
- package/tests/utils/linter/fixtures/python/keyword-error.py +3 -0
- package/tests/utils/linter/fixtures/python/missing-semicolon.py +2 -0
- package/tests/utils/linter/fixtures/python/syntax-error.py +3 -0
- package/tests/utils/linter/fixtures/python/undeclare-variable.py +3 -0
- package/tests/utils/linter/fixtures/python/unused-variable.py +6 -0
- package/tests/utils/linter/fixtures/python/valid-code.py +12 -0
- package/tests/utils/linter/fixtures/ruby/keyword-error.rb +2 -0
- package/tests/utils/linter/fixtures/ruby/missing-semicolon.rb +1 -0
- package/tests/utils/linter/fixtures/ruby/syntax-error.rb +2 -0
- package/tests/utils/linter/fixtures/ruby/undeclare-variable.rb +1 -0
- package/tests/utils/linter/fixtures/ruby/unused-variable.rb +2 -0
- package/tests/utils/linter/fixtures/ruby/valid-code.rb +1 -0
- package/tests/utils/linter/fixtures/sass/keyword-error.sass +2 -0
- package/tests/utils/linter/fixtures/sass/missing-semicolon.sass +3 -0
- package/tests/utils/linter/fixtures/sass/syntax-error.sass +3 -0
- package/tests/utils/linter/fixtures/sass/undeclare-variable.sass +2 -0
- package/tests/utils/linter/fixtures/sass/unused-variable.sass +4 -0
- package/tests/utils/linter/fixtures/sass/valid-code.sass +2 -0
- package/tests/utils/linter/fixtures/scss/keyword-error.scss +1 -0
- package/tests/utils/linter/fixtures/scss/missing-semicolon.scss +1 -0
- package/tests/utils/linter/fixtures/scss/syntax-error.scss +1 -0
- package/tests/utils/linter/fixtures/scss/undeclare-variable.scss +1 -0
- package/tests/utils/linter/fixtures/scss/unused-variable.scss +2 -0
- package/tests/utils/linter/fixtures/scss/valid-code.scss +1 -0
- package/tests/utils/linter/fixtures/shell/keyword-error.sh +5 -0
- package/tests/utils/linter/fixtures/shell/missing-semicolon.sh +3 -0
- package/tests/utils/linter/fixtures/shell/syntax-error.sh +4 -0
- package/tests/utils/linter/fixtures/shell/undeclare-variable.sh +3 -0
- package/tests/utils/linter/fixtures/shell/unused-variable.sh +4 -0
- package/tests/utils/linter/fixtures/shell/valid-code.sh +3 -0
- package/tests/utils/linter/fixtures/ts/keyword-error.ts +1 -0
- package/tests/utils/linter/fixtures/ts/missing-semicolon.ts +1 -0
- package/tests/utils/linter/fixtures/ts/syntax-error.ts +1 -0
- package/tests/utils/linter/fixtures/ts/undeclare-variable.ts +1 -0
- package/tests/utils/linter/fixtures/ts/unused-variable.ts +3 -0
- package/tests/utils/linter/fixtures/ts/valid-code.ts +3 -0
- package/tests/utils/linter/fixtures/tsx/keyword-error.tsx +5 -0
- package/tests/utils/linter/fixtures/tsx/missing-semicolon.tsx +5 -0
- package/tests/utils/linter/fixtures/tsx/syntax-error.tsx +5 -0
- package/tests/utils/linter/fixtures/tsx/undeclare-variable.tsx +6 -0
- package/tests/utils/linter/fixtures/tsx/unused-variable.tsx +6 -0
- package/tests/utils/linter/fixtures/tsx/valid-code.tsx +5 -0
- package/tests/utils/linter/fixtures/vue/keyword-error.vue +6 -0
- package/tests/utils/linter/fixtures/vue/missing-semicolon.vue +6 -0
- package/tests/utils/linter/fixtures/vue/syntax-error.vue +6 -0
- package/tests/utils/linter/fixtures/vue/undeclare-variable.vue +6 -0
- package/tests/utils/linter/fixtures/vue/unused-variable.vue +7 -0
- package/tests/utils/linter/fixtures/vue/valid-code.vue +6 -0
- package/tests/utils/linter/fixtures/yaml/keyword-error.yml +1 -0
- package/tests/utils/linter/fixtures/yaml/missing-semicolon.yml +2 -0
- package/tests/utils/linter/fixtures/yaml/syntax-error.yml +1 -0
- package/tests/utils/linter/fixtures/yaml/undeclare-variable.yml +1 -0
- package/tests/utils/linter/fixtures/yaml/unused-variable.yml +2 -0
- package/tests/utils/linter/fixtures/yaml/valid-code.yml +3 -0
- package/tests/utils/linter/index.test.mjs +440 -0
- package/tests/utils/linter/scan-results.mjs +42 -0
- package/tests/utils/markdown/index.test.mjs +478 -0
- package/tests/utils/mermaid-validator.test.mjs +2 -2
- package/tests/utils/utils.test.mjs +3 -1
- package/types/document-schema.mjs +54 -0
- package/types/document-structure-schema.mjs +244 -0
- package/utils/auth-utils.mjs +131 -6
- package/utils/conflict-detector.mjs +5 -1
- package/utils/{constants.mjs → constants/index.mjs} +109 -0
- package/utils/constants/linter.mjs +102 -0
- package/utils/d2-utils.mjs +2 -4
- package/utils/debug.mjs +3 -0
- package/utils/deploy.mjs +81 -385
- package/utils/evaluate/report-utils.mjs +131 -0
- package/utils/file-utils.mjs +36 -1
- package/utils/kroki-utils.mjs +1 -1
- package/utils/linter/index.mjs +50 -0
- package/utils/markdown/index.mjs +26 -0
- package/utils/markdown-checker.mjs +1 -1
- package/utils/utils.mjs +19 -7
- package/prompts/structure/generate-structure.md +0 -161
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
<role>
|
|
2
|
-
You are a "Feedback→Rule" converter. Transform one-time natural language feedback into a **single sentence**, **executable**, **reusable** instruction,
|
|
3
|
-
and determine whether it needs **persistent saving**, along with its scope (global/structure/document/translation) and whether it should be limited to "input paths range".
|
|
2
|
+
You are a "Feedback→Rule" converter. Transform one-time natural language feedback into a **single sentence**, **executable**, **reusable** instruction, and determine whether it needs **persistent saving**, along with its scope (global/structure/document/translation) and whether it should be limited to "input paths range".
|
|
4
3
|
</role>
|
|
5
4
|
|
|
6
5
|
<input>
|
|
@@ -34,6 +33,7 @@ Save determination rules:
|
|
|
34
33
|
**One-time operations (do not save)**:
|
|
35
34
|
- Only corrects current version/typos/individual phrasing/local factual errors with no stable reusable value → `save=false`
|
|
36
35
|
- Fixes that are highly specific to a single line or data point and unlikely to recur (e.g., "change the year from 2020 to 2021") → `save=false`
|
|
36
|
+
- Document structure adjustments, adding new documents, and moving document positions are always one-time operations → `save=false`
|
|
37
37
|
|
|
38
38
|
**Reusable policies (save)**:
|
|
39
39
|
- Writing styles, structural conventions, inclusion/exclusion items, translation conventions that are broadly applicable and should be consistently executed in the future → `save=true`
|
|
@@ -41,7 +41,7 @@ Save determination rules:
|
|
|
41
41
|
**Duplication check (do not save)**:
|
|
42
42
|
- If `existingPreferences` already contains **similar or covering** rules for current feedback intent, then `save=false`
|
|
43
43
|
- Check logic: Compare feedback intent, rule meaning, and applicable scope. If new feedback is already sufficiently covered by existing rules, no need to save duplicates
|
|
44
|
-
- If new feedback is **refinement, supplement, or conflicting correction** to existing rules,
|
|
44
|
+
- If new feedback is **refinement, supplement, or conflicting correction** to existing rules, it can still be `save=true`
|
|
45
45
|
|
|
46
46
|
**Determination principle**:
|
|
47
47
|
- Prioritize avoiding duplicate saves; if difficult to determine whether duplicate, prioritize `save=false` to avoid rule redundancy
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
+
import chooseContents from "../../../agents/clear/choose-contents.mjs";
|
|
3
|
+
|
|
4
|
+
describe("choose-contents", () => {
|
|
5
|
+
let mockOptions;
|
|
6
|
+
let mockContext;
|
|
7
|
+
let mockClearAgents;
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
// Create mock clear agents
|
|
11
|
+
mockClearAgents = {
|
|
12
|
+
clearGeneratedDocs: mock(async () => ({
|
|
13
|
+
message: "Generated docs cleared",
|
|
14
|
+
cleared: true,
|
|
15
|
+
path: "/test/docs",
|
|
16
|
+
})),
|
|
17
|
+
clearDocumentStructure: mock(async () => ({
|
|
18
|
+
message: "Document structure cleared",
|
|
19
|
+
cleared: true,
|
|
20
|
+
path: "/test/structure.json",
|
|
21
|
+
})),
|
|
22
|
+
clearDocumentConfig: mock(async () => ({
|
|
23
|
+
message: "Document config cleared",
|
|
24
|
+
cleared: true,
|
|
25
|
+
path: "/test/config.yaml",
|
|
26
|
+
suggestions: ["Run `aigne doc init` to generate a fresh configuration file."],
|
|
27
|
+
})),
|
|
28
|
+
clearAuthTokens: mock(async () => ({
|
|
29
|
+
message: "Auth tokens cleared",
|
|
30
|
+
clearedCount: 2,
|
|
31
|
+
clearedSites: ["example.com", "test.com"],
|
|
32
|
+
})),
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
mockContext = {
|
|
36
|
+
agents: mockClearAgents,
|
|
37
|
+
invoke: mock(async (agent, input) => {
|
|
38
|
+
return await agent(input);
|
|
39
|
+
}),
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
mockOptions = {
|
|
43
|
+
prompts: {
|
|
44
|
+
checkbox: mock(async () => ["generatedDocs", "documentConfig"]),
|
|
45
|
+
},
|
|
46
|
+
context: mockContext,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Clear mock call history
|
|
50
|
+
mockOptions.prompts.checkbox.mockClear();
|
|
51
|
+
mockContext.invoke.mockClear();
|
|
52
|
+
Object.values(mockClearAgents).forEach((agent) => {
|
|
53
|
+
agent.mockClear();
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
afterEach(() => {
|
|
58
|
+
// Note: Not using mock.restore() to avoid affecting other tests
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("should process provided targets correctly", async () => {
|
|
62
|
+
const result = await chooseContents(
|
|
63
|
+
{ targets: ["generatedDocs", "documentConfig"] },
|
|
64
|
+
mockOptions,
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
expect(mockContext.invoke).toHaveBeenCalledTimes(2);
|
|
68
|
+
expect(mockClearAgents.clearGeneratedDocs).toHaveBeenCalled();
|
|
69
|
+
expect(mockClearAgents.clearDocumentConfig).toHaveBeenCalled();
|
|
70
|
+
expect(result.message).toContain("Cleanup completed successfully!");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("should normalize target names case-insensitively", async () => {
|
|
74
|
+
const result = await chooseContents(
|
|
75
|
+
{ targets: ["GeneratedDocs", "DOCUMENTCONFIG"] },
|
|
76
|
+
mockOptions,
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
expect(mockContext.invoke).toHaveBeenCalledTimes(2);
|
|
80
|
+
expect(mockClearAgents.clearGeneratedDocs).toHaveBeenCalled();
|
|
81
|
+
expect(mockClearAgents.clearDocumentConfig).toHaveBeenCalled();
|
|
82
|
+
expect(result.message).toContain("Cleanup completed successfully!");
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("should prompt user when no targets provided", async () => {
|
|
86
|
+
mockOptions.prompts.checkbox.mockResolvedValue(["generatedDocs"]);
|
|
87
|
+
|
|
88
|
+
await chooseContents({}, mockOptions);
|
|
89
|
+
|
|
90
|
+
expect(mockOptions.prompts.checkbox).toHaveBeenCalledWith({
|
|
91
|
+
message: "Select items to clear:",
|
|
92
|
+
choices: expect.arrayContaining([
|
|
93
|
+
expect.objectContaining({
|
|
94
|
+
name: "generated documents",
|
|
95
|
+
value: "generatedDocs",
|
|
96
|
+
}),
|
|
97
|
+
]),
|
|
98
|
+
validate: expect.any(Function),
|
|
99
|
+
});
|
|
100
|
+
expect(mockClearAgents.clearGeneratedDocs).toHaveBeenCalled();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("should handle empty selection from prompts", async () => {
|
|
104
|
+
mockOptions.prompts.checkbox.mockResolvedValue([]);
|
|
105
|
+
|
|
106
|
+
const result = await chooseContents({}, mockOptions);
|
|
107
|
+
|
|
108
|
+
expect(result.message).toBe("No items selected to clear.");
|
|
109
|
+
expect(mockContext.invoke).not.toHaveBeenCalled();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("should return available options when no prompts available", async () => {
|
|
113
|
+
const optionsWithoutPrompts = { context: mockContext };
|
|
114
|
+
|
|
115
|
+
const result = await chooseContents({}, optionsWithoutPrompts);
|
|
116
|
+
|
|
117
|
+
expect(result.message).toBe(
|
|
118
|
+
"Available options to clear: generatedDocs, documentStructure, documentConfig, authTokens",
|
|
119
|
+
);
|
|
120
|
+
expect(result.availableTargets).toEqual([
|
|
121
|
+
"generatedDocs",
|
|
122
|
+
"documentStructure",
|
|
123
|
+
"documentConfig",
|
|
124
|
+
"authTokens",
|
|
125
|
+
]);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("should handle agent errors gracefully", async () => {
|
|
129
|
+
mockClearAgents.clearGeneratedDocs.mockResolvedValue({
|
|
130
|
+
error: true,
|
|
131
|
+
message: "Failed to clear docs",
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
const result = await chooseContents({ targets: ["generatedDocs"] }, mockOptions);
|
|
135
|
+
|
|
136
|
+
expect(result.message).toContain("Cleanup finished with some issues.");
|
|
137
|
+
expect(result.message).toContain("Failed to clear docs");
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test("should collect suggestions from agents", async () => {
|
|
141
|
+
const result = await chooseContents({ targets: ["documentConfig"] }, mockOptions);
|
|
142
|
+
|
|
143
|
+
expect(result.message).toContain(
|
|
144
|
+
"Run `aigne doc init` to generate a fresh configuration file.",
|
|
145
|
+
);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test("should handle all valid targets", async () => {
|
|
149
|
+
const result = await chooseContents(
|
|
150
|
+
{ targets: ["generatedDocs", "documentStructure", "documentConfig", "authTokens"] },
|
|
151
|
+
mockOptions,
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
expect(mockContext.invoke).toHaveBeenCalledTimes(4);
|
|
155
|
+
expect(result.message).toContain("Cleanup completed successfully!");
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test("should have correct input schema", () => {
|
|
159
|
+
expect(chooseContents.input_schema).toBeDefined();
|
|
160
|
+
expect(chooseContents.input_schema.type).toBe("object");
|
|
161
|
+
expect(chooseContents.input_schema.properties.targets.type).toBe("array");
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test("should have correct task metadata", () => {
|
|
165
|
+
expect(chooseContents.taskTitle).toBe("Choose contents to clear");
|
|
166
|
+
expect(chooseContents.description).toBe(
|
|
167
|
+
"Choose contents to clear and execute the appropriate clearing operations",
|
|
168
|
+
);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test("should handle unknown targets by filtering them out", async () => {
|
|
172
|
+
// Test that unknown targets are filtered out and valid ones are processed
|
|
173
|
+
// Mix unknown targets with valid ones
|
|
174
|
+
const result = await chooseContents(
|
|
175
|
+
{ targets: ["unknownTarget1", "generatedDocs", "invalidTarget", "documentConfig"] },
|
|
176
|
+
mockOptions,
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
// Should process only the valid targets and succeed
|
|
180
|
+
expect(result.message).toContain("Cleanup completed successfully!");
|
|
181
|
+
expect(mockContext.invoke).toHaveBeenCalledTimes(2); // Only valid targets processed
|
|
182
|
+
expect(mockClearAgents.clearGeneratedDocs).toHaveBeenCalled();
|
|
183
|
+
expect(mockClearAgents.clearDocumentConfig).toHaveBeenCalled();
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
test("should handle missing clear agent in context", async () => {
|
|
187
|
+
const optionsWithoutAgent = {
|
|
188
|
+
context: {
|
|
189
|
+
agents: {},
|
|
190
|
+
invoke: mockContext.invoke,
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const result = await chooseContents({ targets: ["generatedDocs"] }, optionsWithoutAgent);
|
|
195
|
+
|
|
196
|
+
expect(result.message).toContain("Cleanup finished with some issues.");
|
|
197
|
+
expect(result.message).toContain("Clear agent 'clearGeneratedDocs' not found in context");
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test("should handle agent execution errors", async () => {
|
|
201
|
+
const errorContext = {
|
|
202
|
+
agents: mockClearAgents,
|
|
203
|
+
invoke: mock(async () => {
|
|
204
|
+
throw new Error("Agent execution failed");
|
|
205
|
+
}),
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const optionsWithError = {
|
|
209
|
+
context: errorContext,
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const result = await chooseContents({ targets: ["generatedDocs"] }, optionsWithError);
|
|
213
|
+
|
|
214
|
+
expect(result.message).toContain("Cleanup finished with some issues.");
|
|
215
|
+
expect(result.message).toContain("Failed to clear generated documents: Agent execution failed");
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
test("should handle duplicate targets", async () => {
|
|
219
|
+
await chooseContents(
|
|
220
|
+
{ targets: ["generatedDocs", "generatedDocs", "documentConfig"] },
|
|
221
|
+
mockOptions,
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
// Should only invoke each agent once due to deduplication
|
|
225
|
+
expect(mockContext.invoke).toHaveBeenCalledTimes(2);
|
|
226
|
+
expect(mockClearAgents.clearGeneratedDocs).toHaveBeenCalledTimes(1);
|
|
227
|
+
expect(mockClearAgents.clearDocumentConfig).toHaveBeenCalledTimes(1);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
test("should handle empty and whitespace targets", async () => {
|
|
231
|
+
await chooseContents({ targets: ["", " ", "generatedDocs", null, undefined] }, mockOptions);
|
|
232
|
+
|
|
233
|
+
// Should only process the valid target
|
|
234
|
+
expect(mockContext.invoke).toHaveBeenCalledTimes(1);
|
|
235
|
+
expect(mockClearAgents.clearGeneratedDocs).toHaveBeenCalled();
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
test("should add default suggestion when config is cleared", async () => {
|
|
239
|
+
// Mock clearDocumentConfig to return cleared: true but no suggestions
|
|
240
|
+
mockClearAgents.clearDocumentConfig.mockResolvedValue({
|
|
241
|
+
message: "Document config cleared",
|
|
242
|
+
cleared: true,
|
|
243
|
+
path: "/test/config.yaml",
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
const result = await chooseContents({ targets: ["documentConfig"] }, mockOptions);
|
|
247
|
+
|
|
248
|
+
expect(result.message).toContain(
|
|
249
|
+
"Run `aigne doc init` to generate a fresh configuration file.",
|
|
250
|
+
);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
test("should not duplicate default suggestion when already present", async () => {
|
|
254
|
+
// Mock clearDocumentConfig to return the default suggestion
|
|
255
|
+
mockClearAgents.clearDocumentConfig.mockResolvedValue({
|
|
256
|
+
message: "Document config cleared",
|
|
257
|
+
cleared: true,
|
|
258
|
+
path: "/test/config.yaml",
|
|
259
|
+
suggestions: ["Run `aigne doc init` to generate a fresh configuration file."],
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
const result = await chooseContents({ targets: ["documentConfig"] }, mockOptions);
|
|
263
|
+
|
|
264
|
+
// Should only appear once in the message
|
|
265
|
+
const suggestionCount = (result.message.match(/Run `aigne doc init`/g) || []).length;
|
|
266
|
+
expect(suggestionCount).toBe(1);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
test("should handle agents that return no cleared status", async () => {
|
|
270
|
+
mockClearAgents.clearGeneratedDocs.mockResolvedValue({
|
|
271
|
+
message: "No docs to clear",
|
|
272
|
+
// No cleared property
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
const result = await chooseContents({ targets: ["generatedDocs"] }, mockOptions);
|
|
276
|
+
|
|
277
|
+
expect(result.message).toContain("Cleanup completed successfully!");
|
|
278
|
+
expect(result.message).toContain("No docs to clear");
|
|
279
|
+
});
|
|
280
|
+
});
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, mock, spyOn, test } from "bun:test";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import * as fsPromises from "node:fs/promises";
|
|
4
|
+
import clearAuthTokens from "../../../agents/clear/clear-auth-tokens.mjs";
|
|
5
|
+
|
|
6
|
+
describe("clear-auth-tokens", () => {
|
|
7
|
+
let mockOptions;
|
|
8
|
+
let existsSyncSpy;
|
|
9
|
+
let readFileSpy;
|
|
10
|
+
let writeFileSpy;
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
mockOptions = {
|
|
14
|
+
prompts: {
|
|
15
|
+
checkbox: mock(async () => ["example.com"]),
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// Mock file system operations
|
|
20
|
+
existsSyncSpy = spyOn(fs, "existsSync");
|
|
21
|
+
readFileSpy = spyOn(fsPromises, "readFile");
|
|
22
|
+
writeFileSpy = spyOn(fsPromises, "writeFile");
|
|
23
|
+
|
|
24
|
+
// Clear mock call history
|
|
25
|
+
mockOptions.prompts.checkbox.mockClear();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
// Restore all mocks
|
|
30
|
+
existsSyncSpy.mockRestore();
|
|
31
|
+
readFileSpy.mockRestore();
|
|
32
|
+
writeFileSpy.mockRestore();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("should accept empty input", async () => {
|
|
36
|
+
// Mock file doesn't exist
|
|
37
|
+
existsSyncSpy.mockReturnValue(false);
|
|
38
|
+
|
|
39
|
+
const result = await clearAuthTokens({}, {});
|
|
40
|
+
expect(result).toBeDefined();
|
|
41
|
+
expect(result.message).toBeDefined();
|
|
42
|
+
expect(typeof result.message).toBe("string");
|
|
43
|
+
expect(result.message).toBe("No site authorizations found to clear");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("should handle missing prompts gracefully", async () => {
|
|
47
|
+
// Mock file exists with some data
|
|
48
|
+
existsSyncSpy.mockReturnValue(true);
|
|
49
|
+
readFileSpy.mockResolvedValue('example.com:\n token: "test-token"');
|
|
50
|
+
writeFileSpy.mockResolvedValue();
|
|
51
|
+
|
|
52
|
+
const result = await clearAuthTokens({}, {});
|
|
53
|
+
expect(result).toBeDefined();
|
|
54
|
+
expect(result.message).toBeDefined();
|
|
55
|
+
expect(result.clearedCount).toBe(1);
|
|
56
|
+
expect(result.clearedSites).toEqual(["example.com"]);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test("should handle empty options", async () => {
|
|
60
|
+
// Mock file doesn't exist
|
|
61
|
+
existsSyncSpy.mockReturnValue(false);
|
|
62
|
+
|
|
63
|
+
const result = await clearAuthTokens({});
|
|
64
|
+
expect(result).toBeDefined();
|
|
65
|
+
expect(result.message).toBeDefined();
|
|
66
|
+
expect(result.message).toBe("No site authorizations found to clear");
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("should return consistent result structure", async () => {
|
|
70
|
+
// Mock file exists with multiple sites
|
|
71
|
+
existsSyncSpy.mockReturnValue(true);
|
|
72
|
+
readFileSpy.mockResolvedValue(
|
|
73
|
+
'example.com:\n token: "test-token"\ntest.com:\n token: "test-token2"',
|
|
74
|
+
);
|
|
75
|
+
writeFileSpy.mockResolvedValue();
|
|
76
|
+
|
|
77
|
+
const result = await clearAuthTokens({}, mockOptions);
|
|
78
|
+
expect(result).toBeDefined();
|
|
79
|
+
expect(result).toHaveProperty("message");
|
|
80
|
+
expect(typeof result.message).toBe("string");
|
|
81
|
+
|
|
82
|
+
// Result may have additional properties depending on file state
|
|
83
|
+
if (result.clearedCount !== undefined) {
|
|
84
|
+
expect(typeof result.clearedCount).toBe("number");
|
|
85
|
+
}
|
|
86
|
+
if (result.clearedSites !== undefined) {
|
|
87
|
+
expect(Array.isArray(result.clearedSites)).toBe(true);
|
|
88
|
+
}
|
|
89
|
+
if (result.error !== undefined) {
|
|
90
|
+
expect(typeof result.error).toBe("boolean");
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("should handle prompts correctly when file exists (integration test)", async () => {
|
|
95
|
+
// Mock file exists with data
|
|
96
|
+
existsSyncSpy.mockReturnValue(true);
|
|
97
|
+
readFileSpy.mockResolvedValue('example.com:\n token: "test-token"');
|
|
98
|
+
writeFileSpy.mockResolvedValue();
|
|
99
|
+
|
|
100
|
+
const result = await clearAuthTokens({}, mockOptions);
|
|
101
|
+
|
|
102
|
+
expect(result).toBeDefined();
|
|
103
|
+
expect(result.message).toBeDefined();
|
|
104
|
+
expect(result.clearedCount).toBe(1);
|
|
105
|
+
expect(result.clearedSites).toEqual(["example.com"]);
|
|
106
|
+
|
|
107
|
+
// Verify that prompts were called
|
|
108
|
+
expect(mockOptions.prompts.checkbox.mock.calls.length).toBeGreaterThan(0);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("should provide meaningful error messages", async () => {
|
|
112
|
+
// Mock file doesn't exist
|
|
113
|
+
existsSyncSpy.mockReturnValue(false);
|
|
114
|
+
|
|
115
|
+
const result = await clearAuthTokens({}, {});
|
|
116
|
+
|
|
117
|
+
// Should return a user-friendly message regardless of internal state
|
|
118
|
+
expect(result.message).toBeDefined();
|
|
119
|
+
expect(result.message.length).toBeGreaterThan(0);
|
|
120
|
+
expect(typeof result.message).toBe("string");
|
|
121
|
+
expect(result.message).toBe("No site authorizations found to clear");
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("should handle undefined input parameters", async () => {
|
|
125
|
+
// Mock file doesn't exist
|
|
126
|
+
existsSyncSpy.mockReturnValue(false);
|
|
127
|
+
|
|
128
|
+
const result = await clearAuthTokens(undefined, undefined);
|
|
129
|
+
expect(result).toBeDefined();
|
|
130
|
+
expect(result.message).toBeDefined();
|
|
131
|
+
expect(result.message).toBe("No site authorizations found to clear");
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test("should handle null input parameters", async () => {
|
|
135
|
+
// Mock file doesn't exist
|
|
136
|
+
existsSyncSpy.mockReturnValue(false);
|
|
137
|
+
|
|
138
|
+
const result = await clearAuthTokens(null, null);
|
|
139
|
+
expect(result).toBeDefined();
|
|
140
|
+
expect(result.message).toBeDefined();
|
|
141
|
+
expect(result.message).toBe("No site authorizations found to clear");
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("should handle various option configurations", async () => {
|
|
145
|
+
// Mock file doesn't exist for all configurations
|
|
146
|
+
existsSyncSpy.mockReturnValue(false);
|
|
147
|
+
|
|
148
|
+
const configs = [
|
|
149
|
+
{},
|
|
150
|
+
{ prompts: {} },
|
|
151
|
+
{ prompts: { checkbox: undefined } },
|
|
152
|
+
{ prompts: { checkbox: null } },
|
|
153
|
+
];
|
|
154
|
+
|
|
155
|
+
for (const config of configs) {
|
|
156
|
+
const result = await clearAuthTokens({}, config);
|
|
157
|
+
expect(result).toBeDefined();
|
|
158
|
+
expect(result.message).toBeDefined();
|
|
159
|
+
expect(result.message).toBe("No site authorizations found to clear");
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test("should maintain consistent behavior across calls", async () => {
|
|
164
|
+
// Mock file doesn't exist for both calls
|
|
165
|
+
existsSyncSpy.mockReturnValue(false);
|
|
166
|
+
|
|
167
|
+
const result1 = await clearAuthTokens({}, {});
|
|
168
|
+
const result2 = await clearAuthTokens({}, {});
|
|
169
|
+
|
|
170
|
+
// Both calls should return the same type of result structure
|
|
171
|
+
expect(typeof result1.message).toBe(typeof result2.message);
|
|
172
|
+
expect(result1).toHaveProperty("message");
|
|
173
|
+
expect(result2).toHaveProperty("message");
|
|
174
|
+
expect(result1.message).toBe(result2.message);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("should handle prompt validation function", async () => {
|
|
178
|
+
// Mock file exists with data to trigger prompts
|
|
179
|
+
existsSyncSpy.mockReturnValue(true);
|
|
180
|
+
readFileSpy.mockResolvedValue('example.com:\n token: "test-token"');
|
|
181
|
+
writeFileSpy.mockResolvedValue();
|
|
182
|
+
|
|
183
|
+
// Test that if prompts are called, they have proper validation
|
|
184
|
+
try {
|
|
185
|
+
await clearAuthTokens({}, mockOptions);
|
|
186
|
+
|
|
187
|
+
// If prompts were called, check the validation function exists
|
|
188
|
+
if (mockOptions.prompts.checkbox.mock.calls.length > 0) {
|
|
189
|
+
const callArgs = mockOptions.prompts.checkbox.mock.calls[0][0];
|
|
190
|
+
expect(callArgs).toHaveProperty("validate");
|
|
191
|
+
expect(typeof callArgs.validate).toBe("function");
|
|
192
|
+
|
|
193
|
+
// Test validation function behavior
|
|
194
|
+
const validateFn = callArgs.validate;
|
|
195
|
+
expect(validateFn([])).toBe("Please select at least one site.");
|
|
196
|
+
expect(validateFn(["site1"])).toBe(true);
|
|
197
|
+
expect(validateFn(["site1", "site2"])).toBe(true);
|
|
198
|
+
}
|
|
199
|
+
} catch (error) {
|
|
200
|
+
// If there's an error, ensure it's handled gracefully
|
|
201
|
+
expect(error).toBeDefined();
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test("should handle file read errors gracefully", async () => {
|
|
206
|
+
// Mock file exists but reading fails
|
|
207
|
+
existsSyncSpy.mockReturnValue(true);
|
|
208
|
+
readFileSpy.mockRejectedValue(new Error("Permission denied"));
|
|
209
|
+
|
|
210
|
+
const result = await clearAuthTokens({}, {});
|
|
211
|
+
expect(result).toBeDefined();
|
|
212
|
+
expect(result.message).toBeDefined();
|
|
213
|
+
expect(result.error).toBe(true);
|
|
214
|
+
expect(result.message).toContain("Failed to clear site authorizations");
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
test("should handle file write errors gracefully", async () => {
|
|
218
|
+
// Mock file exists, reading succeeds, but writing fails
|
|
219
|
+
existsSyncSpy.mockReturnValue(true);
|
|
220
|
+
readFileSpy.mockResolvedValue('example.com:\n token: "test-token"');
|
|
221
|
+
writeFileSpy.mockRejectedValue(new Error("Disk full"));
|
|
222
|
+
|
|
223
|
+
const result = await clearAuthTokens({}, {});
|
|
224
|
+
expect(result).toBeDefined();
|
|
225
|
+
expect(result.message).toBeDefined();
|
|
226
|
+
expect(result.error).toBe(true);
|
|
227
|
+
expect(result.message).toContain("Failed to clear site authorizations");
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
test("should handle empty file content", async () => {
|
|
231
|
+
// Mock file exists but is empty
|
|
232
|
+
existsSyncSpy.mockReturnValue(true);
|
|
233
|
+
readFileSpy.mockResolvedValue("");
|
|
234
|
+
|
|
235
|
+
const result = await clearAuthTokens({}, {});
|
|
236
|
+
expect(result).toBeDefined();
|
|
237
|
+
expect(result.message).toBeDefined();
|
|
238
|
+
expect(result.message).toBe("No site authorizations found to clear");
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
test("should handle malformed YAML content", async () => {
|
|
242
|
+
// Mock file exists with invalid YAML
|
|
243
|
+
existsSyncSpy.mockReturnValue(true);
|
|
244
|
+
readFileSpy.mockResolvedValue("invalid: yaml: content: [");
|
|
245
|
+
|
|
246
|
+
const result = await clearAuthTokens({}, {});
|
|
247
|
+
expect(result).toBeDefined();
|
|
248
|
+
expect(result.message).toBeDefined();
|
|
249
|
+
expect(result.error).toBe(true);
|
|
250
|
+
expect(result.message).toContain("Failed to clear site authorizations");
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
test("should handle empty selection from prompts", async () => {
|
|
254
|
+
// Mock file exists with data but user selects nothing
|
|
255
|
+
existsSyncSpy.mockReturnValue(true);
|
|
256
|
+
readFileSpy.mockResolvedValue('example.com:\n token: "test-token"');
|
|
257
|
+
|
|
258
|
+
const emptySelectionOptions = {
|
|
259
|
+
prompts: {
|
|
260
|
+
checkbox: mock(async () => []), // User selects nothing
|
|
261
|
+
},
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
const result = await clearAuthTokens({}, emptySelectionOptions);
|
|
265
|
+
expect(result).toBeDefined();
|
|
266
|
+
expect(result.message).toBe("No sites selected for clearing authorization");
|
|
267
|
+
});
|
|
268
|
+
});
|