diff --git a/.talismanrc b/.talismanrc index 7acc781452..00aae7342c 100644 --- a/.talismanrc +++ b/.talismanrc @@ -1,6 +1,6 @@ fileignoreconfig: - filename: package-lock.json - checksum: 87af2bc0ae8f9511eb8c7f939898bf24f81f87d54fdb2a4a35422a7f601238d7 + checksum: 6ff9c8334d085a39cbda0377f9b36f1af3f3735f62d9372c0e51efaa4f4a960e - filename: pnpm-lock.yaml checksum: 55c56cfbb8057c4586594bf99ccc68e1f171fbf77ea49a5934ba7d2c52a2626a - filename: packages/contentstack-import-setup/test/unit/backup-handler.test.ts diff --git a/package-lock.json b/package-lock.json index d20c0755a6..8c38a43885 100644 --- a/package-lock.json +++ b/package-lock.json @@ -280,16 +280,16 @@ } }, "node_modules/@aws-sdk/client-cloudfront": { - "version": "3.916.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-cloudfront/-/client-cloudfront-3.916.0.tgz", - "integrity": "sha512-5EnPpehyVkyyeRDUkaWZrAizkbKw0Awp8L6349UBFKh+GfHQdfh+ETU+mKUYyPqmvMd6uRWxIkrbDvPE0nJj+A==", + "version": "3.917.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cloudfront/-/client-cloudfront-3.917.0.tgz", + "integrity": "sha512-ZnbhUpnVWh/E0wWw0PygCq8fj7Pytun29Pu3PqIl6Qh9d0XU5kx0Ecis0vNi9HWqj/jmJ5+UDiUcVxC2ft0Utw==", "dev": true, "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.916.0", - "@aws-sdk/credential-provider-node": "3.916.0", + "@aws-sdk/credential-provider-node": "3.917.0", "@aws-sdk/middleware-host-header": "3.914.0", "@aws-sdk/middleware-logger": "3.914.0", "@aws-sdk/middleware-recursion-detection": "3.914.0", @@ -334,9 +334,9 @@ } }, "node_modules/@aws-sdk/client-s3": { - "version": "3.916.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.916.0.tgz", - "integrity": "sha512-myfO8UkJzF3wxLUV1cKzzxI1oVOe+tsEyUypFt8yrs0WT0usNfjpUOmA4XNjp/wRClpImkEHT0XC1p6xQCuktQ==", + "version": "3.917.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.917.0.tgz", + "integrity": "sha512-3L73mDCpH7G0koFv3p3WkkEKqC5wn2EznKtNMrJ6hczPIr2Cu6DJz8VHeTZp9wFZLPrIBmh3ZW1KiLujT5Fd2w==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -344,9 +344,9 @@ "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.916.0", - "@aws-sdk/credential-provider-node": "3.916.0", + "@aws-sdk/credential-provider-node": "3.917.0", "@aws-sdk/middleware-bucket-endpoint": "3.914.0", - "@aws-sdk/middleware-expect-continue": "3.916.0", + "@aws-sdk/middleware-expect-continue": "3.917.0", "@aws-sdk/middleware-flexible-checksums": "3.916.0", "@aws-sdk/middleware-host-header": "3.914.0", "@aws-sdk/middleware-location-constraint": "3.914.0", @@ -517,9 +517,9 @@ } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.916.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.916.0.tgz", - "integrity": "sha512-iR0FofvdPs87o6MhfNPv0F6WzB4VZ9kx1hbvmR7bSFCk7l0gc7G4fHJOg4xg2lsCptuETboX3O/78OQ2Djeakw==", + "version": "3.917.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.917.0.tgz", + "integrity": "sha512-rvQ0QamLySRq+Okc0ZqFHZ3Fbvj3tYuWNIlzyEKklNmw5X5PM1idYKlOJflY2dvUGkIqY3lUC9SC2WL+1s7KIw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -528,7 +528,7 @@ "@aws-sdk/credential-provider-http": "3.916.0", "@aws-sdk/credential-provider-process": "3.916.0", "@aws-sdk/credential-provider-sso": "3.916.0", - "@aws-sdk/credential-provider-web-identity": "3.916.0", + "@aws-sdk/credential-provider-web-identity": "3.917.0", "@aws-sdk/nested-clients": "3.916.0", "@aws-sdk/types": "3.914.0", "@smithy/credential-provider-imds": "^4.2.3", @@ -542,18 +542,18 @@ } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.916.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.916.0.tgz", - "integrity": "sha512-8TrMpHqct0zTalf2CP2uODiN/PH9LPdBC6JDgPVK0POELTT4ITHerMxIhYGEiKN+6E4oRwSjM/xVTHCD4nMcrQ==", + "version": "3.917.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.917.0.tgz", + "integrity": "sha512-n7HUJ+TgU9wV/Z46yR1rqD9hUjfG50AKi+b5UXTlaDlVD8bckg40i77ROCllp53h32xQj/7H0yBIYyphwzLtmg==", "dev": true, "license": "Apache-2.0", "dependencies": { "@aws-sdk/credential-provider-env": "3.916.0", "@aws-sdk/credential-provider-http": "3.916.0", - "@aws-sdk/credential-provider-ini": "3.916.0", + "@aws-sdk/credential-provider-ini": "3.917.0", "@aws-sdk/credential-provider-process": "3.916.0", "@aws-sdk/credential-provider-sso": "3.916.0", - "@aws-sdk/credential-provider-web-identity": "3.916.0", + "@aws-sdk/credential-provider-web-identity": "3.917.0", "@aws-sdk/types": "3.914.0", "@smithy/credential-provider-imds": "^4.2.3", "@smithy/property-provider": "^4.2.3", @@ -604,9 +604,9 @@ } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.916.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.916.0.tgz", - "integrity": "sha512-VFnL1EjHiwqi2kR19MLXjEgYBuWViCuAKLGSFGSzfFF/+kSpamVrOSFbqsTk8xwHan8PyNnQg4BNuusXwwLoIw==", + "version": "3.917.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.917.0.tgz", + "integrity": "sha512-pZncQhFbwW04pB0jcD5OFv3x2gAddDYCVxyJVixgyhSw7bKCYxqu6ramfq1NxyVpmm+qsw+ijwi/3cCmhUHF/A==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -642,9 +642,9 @@ } }, "node_modules/@aws-sdk/middleware-expect-continue": { - "version": "3.916.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.916.0.tgz", - "integrity": "sha512-p7TMLZZ/j5NbC7/cz7xNgxLz/OHYuh91MeCZdCedJiyh3rx6gunFtl9eiDtrh+Y8hjs0EwR0zYIuhd6pL1O8zg==", + "version": "3.917.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.917.0.tgz", + "integrity": "sha512-UPBq1ZP2CaxwbncWSbVqkhYXQrmfNiqAtHyBxi413hjRVZ4JhQ1UyH7pz5yqiG8zx2/+Po8cUD4SDUwJgda4nw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3747,9 +3747,9 @@ } }, "node_modules/@oclif/plugin-help": { - "version": "6.2.33", - "resolved": "https://registry.npmjs.org/@oclif/plugin-help/-/plugin-help-6.2.33.tgz", - "integrity": "sha512-9L07S61R0tuXrURdLcVtjF79Nbyv3qGplJ88DVskJBxShbROZl3hBG7W/CNltAK3cnMPlXV8K3kKh+C0N0p4xw==", + "version": "6.2.34", + "resolved": "https://registry.npmjs.org/@oclif/plugin-help/-/plugin-help-6.2.34.tgz", + "integrity": "sha512-RvcDSp1PcXFuPJx8IvkI1sQKAPp7TuR+4QVg+uS+Dv3xG6QSqGW5IMNBdvfmB2NLrvSeIiDHadLv/bz9n4iQWQ==", "license": "MIT", "dependencies": { "@oclif/core": "^4" @@ -3759,13 +3759,13 @@ } }, "node_modules/@oclif/plugin-not-found": { - "version": "3.2.70", - "resolved": "https://registry.npmjs.org/@oclif/plugin-not-found/-/plugin-not-found-3.2.70.tgz", - "integrity": "sha512-pFU32i0hpOrpb2k+HXTp2MuGB/FaaTDrbCkbcoA+0uxjGAqhifxCJlDLZI/BCjsjd0nKJ0pZEDbiIAA6+2oKoA==", + "version": "3.2.71", + "resolved": "https://registry.npmjs.org/@oclif/plugin-not-found/-/plugin-not-found-3.2.71.tgz", + "integrity": "sha512-Vp93vWBzAyZFYtovQtAH3lBAtJE8Z0XUYu1/3uN2Y1kE7ywCNnivaEYRw8n4D3G4uF1g4GaXKAQP+HiYL/d2Ug==", "license": "MIT", "dependencies": { "@inquirer/prompts": "^7.9.0", - "@oclif/core": "^4.5.6", + "@oclif/core": "^4.7.2", "ansis": "^3.17.0", "fast-levenshtein": "^3.0.0" }, @@ -4175,12 +4175,12 @@ } }, "node_modules/@oclif/plugin-plugins": { - "version": "5.4.50", - "resolved": "https://registry.npmjs.org/@oclif/plugin-plugins/-/plugin-plugins-5.4.50.tgz", - "integrity": "sha512-HNhmmgxH0xoFsYKubtWWhgSasbDEyoT+o/q5QDljiytNvqWP3wWiP6cqqWvGpKG2El7+g17crdWpv4jzrf3Lyg==", + "version": "5.4.51", + "resolved": "https://registry.npmjs.org/@oclif/plugin-plugins/-/plugin-plugins-5.4.51.tgz", + "integrity": "sha512-n9WT0MSw6mQyZOAiMeRDZIhz3l1OKbkyviR5IEWgrkP0lKZz5+0t3jWKHLp45US1sg/42YWzkuo7/m4MLvfxkQ==", "license": "MIT", "dependencies": { - "@oclif/core": "^4.5.4", + "@oclif/core": "^4.7.2", "ansis": "^3.17.0", "debug": "^4.4.0", "npm": "^10.9.4", @@ -4197,9 +4197,9 @@ } }, "node_modules/@oclif/plugin-warn-if-update-available": { - "version": "3.1.50", - "resolved": "https://registry.npmjs.org/@oclif/plugin-warn-if-update-available/-/plugin-warn-if-update-available-3.1.50.tgz", - "integrity": "sha512-JAN0qm5z4FrgZ5i1K1vDGCglOTYrdHtSwSi0R6EAqv0SlrlY5ZKDqpRFklT0i2KGr4M6XPoDr1QiDsZbpN62EQ==", + "version": "3.1.51", + "resolved": "https://registry.npmjs.org/@oclif/plugin-warn-if-update-available/-/plugin-warn-if-update-available-3.1.51.tgz", + "integrity": "sha512-++PpRVemEasTc8X54EL4Td0BQz+DzRilWofUxmzVHnZGJsXcM8e9VdoKkrk5yUs/7sO+MqJm17Yvsk7JHqcN3A==", "dev": true, "license": "MIT", "dependencies": { @@ -4333,9 +4333,9 @@ } }, "node_modules/@rollup/plugin-commonjs": { - "version": "28.0.8", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.8.tgz", - "integrity": "sha512-o1Ug9PxYsF61R7/NXO/GgMZZproLd/WH2XA53Tp9ppf6bU1lMlTtC/gUM6zM3mesi2E0rypk+PNtVrELREyWEQ==", + "version": "28.0.9", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.9.tgz", + "integrity": "sha512-PIR4/OHZ79romx0BVVll/PkwWpJ7e5lsqFa3gFfcrFPWwLXLV39JVUzQV9RKjWerE7B845Hqjj9VYlQeieZ2dA==", "license": "MIT", "dependencies": { "@rollup/pluginutils": "^5.0.1", @@ -10088,15 +10088,15 @@ } }, "node_modules/eslint-config-oclif": { - "version": "6.0.110", - "resolved": "https://registry.npmjs.org/eslint-config-oclif/-/eslint-config-oclif-6.0.110.tgz", - "integrity": "sha512-rSabSewuQudsmSSYjUnYuzROlObc4ESjPYS7L5HS9yUR7VXhX2l/jX2e5C6K6x650Mj/Q0/bd8LEbZyO4AvvCQ==", + "version": "6.0.114", + "resolved": "https://registry.npmjs.org/eslint-config-oclif/-/eslint-config-oclif-6.0.114.tgz", + "integrity": "sha512-KBl29BbP9dELCBqiF0fVNXp1tDB97ZWEjW2mXzDWkvvVWdDxdrmCM5nV4PJdnzlaGQ559FwfbUS2rWag4VsvAQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint/compat": "^1.4.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "^9.34.0", + "@eslint/js": "^9.38.0", "@stylistic/eslint-plugin": "^3.1.0", "@typescript-eslint/eslint-plugin": "^8", "@typescript-eslint/parser": "^8", @@ -10110,7 +10110,7 @@ "eslint-plugin-n": "^17.22.0", "eslint-plugin-perfectionist": "^4", "eslint-plugin-unicorn": "^56.0.1", - "typescript-eslint": "^8.46.0" + "typescript-eslint": "^8.46.2" }, "engines": { "node": ">=18.18.0" @@ -13574,9 +13574,9 @@ } }, "node_modules/immer": { - "version": "10.1.3", - "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.3.tgz", - "integrity": "sha512-tmjF/k8QDKydUlm3mZU+tjM6zeq9/fFpPqH9SzWmBnVVKsPBg/V66qsMwb3/Bo90cgUN+ghdVBess+hPsxUyRw==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.2.0.tgz", + "integrity": "sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==", "license": "MIT", "funding": { "type": "opencollective", @@ -20900,21 +20900,21 @@ } }, "node_modules/oclif": { - "version": "4.22.32", - "resolved": "https://registry.npmjs.org/oclif/-/oclif-4.22.32.tgz", - "integrity": "sha512-zeM5Ezgh2Eo+dw5gPByyPmpoHBH6i0Lv0I8QrWwyphAHsR1PtSqIOwm24I8jzE0iiZuqKOlhMivLruMrLWfhXg==", + "version": "4.22.38", + "resolved": "https://registry.npmjs.org/oclif/-/oclif-4.22.38.tgz", + "integrity": "sha512-h9DiPdiu61/NjBqBQroSZ+cRhcaQZuXUmUejmbYoNZ+yASthZ88fAY2GkR4vfEDUt7pLVXpJYmoLulM2Nl3TWA==", "dev": true, "license": "MIT", "dependencies": { - "@aws-sdk/client-cloudfront": "^3.908.0", - "@aws-sdk/client-s3": "^3.901.0", + "@aws-sdk/client-cloudfront": "^3.917.0", + "@aws-sdk/client-s3": "^3.913.0", "@inquirer/confirm": "^3.1.22", "@inquirer/input": "^2.2.4", "@inquirer/select": "^2.5.0", "@oclif/core": "^4.5.5", - "@oclif/plugin-help": "^6.2.33", - "@oclif/plugin-not-found": "^3.2.68", - "@oclif/plugin-warn-if-update-available": "^3.1.48", + "@oclif/plugin-help": "^6.2.34", + "@oclif/plugin-not-found": "^3.2.71", + "@oclif/plugin-warn-if-update-available": "^3.1.50", "ansis": "^3.16.0", "async-retry": "^1.3.3", "change-case": "^4", diff --git a/packages/contentstack-import/test/unit/import/modules/stack.test.ts b/packages/contentstack-import/test/unit/import/modules/stack.test.ts new file mode 100644 index 0000000000..e850c33761 --- /dev/null +++ b/packages/contentstack-import/test/unit/import/modules/stack.test.ts @@ -0,0 +1,443 @@ +import { expect } from 'chai'; +import * as sinon from 'sinon'; +import ImportStack from '../../../../src/import/modules/stack'; +import { ImportConfig } from '../../../../src/types'; +import * as fs from 'fs'; +import * as path from 'path'; + +describe('ImportStack', () => { + let importStack: ImportStack; + let mockImportConfig: ImportConfig; + let sandbox: sinon.SinonSandbox; + let tempDir: string; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + + // Create a temporary directory for testing + tempDir = '/tmp/test-backup'; + + mockImportConfig = { + backupDir: tempDir, + target_stack: 'test-stack-uid', + org_uid: 'test-org-uid', + management_token: undefined, + context: { + module: 'stack', + stack: 'test-stack', + management_token: 'test-token' + } + } as any; + + importStack = new ImportStack({ + importConfig: mockImportConfig, + stackAPIClient: {} as any, + moduleName: 'stack' as any + }); + }); + + afterEach(() => { + sandbox.restore(); + // Clean up temp files + try { + if (fs.existsSync(tempDir)) { + fs.rmSync(tempDir, { recursive: true, force: true }); + } + } catch (error) { + // Ignore cleanup errors + } + }); + + describe('Constructor', () => { + it('should initialize with correct config and paths', () => { + expect(importStack.importConfig).to.deep.equal(mockImportConfig); + expect((importStack as any).stackSettingsPath).to.equal(path.join(tempDir, 'stack', 'settings.json')); + expect((importStack as any).envUidMapperPath).to.equal(path.join(tempDir, 'mapper', 'environments', 'uid-mapping.json')); + }); + + it('should initialize with null stackSettings and empty envUidMapper', () => { + expect((importStack as any).stackSettings).to.be.null; + expect((importStack as any).envUidMapper).to.deep.equal({}); + }); + }); + + describe('start method', () => { + it('should skip import when management_token is present', async () => { + mockImportConfig.management_token = 'test-token'; + + const logSpy = sandbox.spy(console, 'log'); + + await importStack.start(); + + // The method should return early without doing anything + expect((importStack as any).stackSettings).to.be.null; + expect((importStack as any).envUidMapper).to.deep.equal({}); + }); + + it('should skip import when stack settings file does not exist', async () => { + // Don't create the stack settings file + const logSpy = sandbox.spy(console, 'log'); + + await importStack.start(); + + expect((importStack as any).stackSettings).to.be.null; + }); + + it('should skip import when environment UID mapper file does not exist', async () => { + // Create stack settings file but not env mapper + fs.mkdirSync(path.dirname((importStack as any).stackSettingsPath), { recursive: true }); + fs.writeFileSync((importStack as any).stackSettingsPath, JSON.stringify({ some: 'settings' })); + + const logSpy = sandbox.spy(console, 'log'); + + await importStack.start(); + + expect((importStack as any).stackSettings).to.deep.equal({ some: 'settings' }); + }); + + it('should successfully import stack settings without live preview', async () => { + // Create both required files + fs.mkdirSync(path.dirname((importStack as any).stackSettingsPath), { recursive: true }); + fs.mkdirSync(path.dirname((importStack as any).envUidMapperPath), { recursive: true }); + + fs.writeFileSync((importStack as any).stackSettingsPath, JSON.stringify({ some: 'settings' })); + fs.writeFileSync((importStack as any).envUidMapperPath, JSON.stringify({ 'env1': 'new-env1' })); + + // Mock the stack.addSettings method + const stackStub = sandbox.stub().resolves(); + sandbox.stub(importStack, 'stack').value({ addSettings: stackStub }); + + const logSpy = sandbox.spy(console, 'log'); + + await importStack.start(); + + expect(stackStub.calledWith({ some: 'settings' })).to.be.true; + expect((importStack as any).stackSettings).to.deep.equal({ some: 'settings' }); + expect((importStack as any).envUidMapper).to.deep.equal({ 'env1': 'new-env1' }); + }); + + it('should successfully import stack settings with live preview and environment mapping', async () => { + // Create both required files + fs.mkdirSync(path.dirname((importStack as any).stackSettingsPath), { recursive: true }); + fs.mkdirSync(path.dirname((importStack as any).envUidMapperPath), { recursive: true }); + + const stackSettings = { + live_preview: { + 'default-env': 'old-env-uid', + other: 'settings' + } + }; + + fs.writeFileSync((importStack as any).stackSettingsPath, JSON.stringify(stackSettings)); + fs.writeFileSync((importStack as any).envUidMapperPath, JSON.stringify({ 'old-env-uid': 'new-env-uid' })); + + // Mock the stack.addSettings method + const stackStub = sandbox.stub().resolves(); + sandbox.stub(importStack, 'stack').value({ addSettings: stackStub }); + + const logSpy = sandbox.spy(console, 'log'); + + await importStack.start(); + + const expectedSettings = { + live_preview: { + 'default-env': 'new-env-uid', + other: 'settings' + } + }; + expect(stackStub.calledWith(expectedSettings)).to.be.true; + }); + + it('should handle live preview without default-env', async () => { + // Create both required files + fs.mkdirSync(path.dirname((importStack as any).stackSettingsPath), { recursive: true }); + fs.mkdirSync(path.dirname((importStack as any).envUidMapperPath), { recursive: true }); + + const stackSettings = { + live_preview: { + other: 'settings' + } + }; + + fs.writeFileSync((importStack as any).stackSettingsPath, JSON.stringify(stackSettings)); + fs.writeFileSync((importStack as any).envUidMapperPath, JSON.stringify({ 'env1': 'new-env1' })); + + // Mock the stack.addSettings method + const stackStub = sandbox.stub().resolves(); + sandbox.stub(importStack, 'stack').value({ addSettings: stackStub }); + + const logSpy = sandbox.spy(console, 'log'); + + await importStack.start(); + + expect(stackStub.calledWith(stackSettings)).to.be.true; + }); + + it('should handle live preview with default-env but no mapping found', async () => { + // Create both required files + fs.mkdirSync(path.dirname((importStack as any).stackSettingsPath), { recursive: true }); + fs.mkdirSync(path.dirname((importStack as any).envUidMapperPath), { recursive: true }); + + const stackSettings = { + live_preview: { + 'default-env': 'unknown-env-uid', + other: 'settings' + } + }; + + fs.writeFileSync((importStack as any).stackSettingsPath, JSON.stringify(stackSettings)); + fs.writeFileSync((importStack as any).envUidMapperPath, JSON.stringify({ 'env1': 'new-env1' })); + + // Mock the stack.addSettings method + const stackStub = sandbox.stub().resolves(); + sandbox.stub(importStack, 'stack').value({ addSettings: stackStub }); + + const logSpy = sandbox.spy(console, 'log'); + + await importStack.start(); + + const expectedSettings = { + live_preview: { + 'default-env': undefined as any, + other: 'settings' + } + }; + expect(stackStub.calledWith(expectedSettings)).to.be.true; + }); + + it('should handle stack settings import error', async () => { + // Create both required files + fs.mkdirSync(path.dirname((importStack as any).stackSettingsPath), { recursive: true }); + fs.mkdirSync(path.dirname((importStack as any).envUidMapperPath), { recursive: true }); + + fs.writeFileSync((importStack as any).stackSettingsPath, JSON.stringify({ some: 'settings' })); + fs.writeFileSync((importStack as any).envUidMapperPath, JSON.stringify({ 'env1': 'new-env1' })); + + // Mock the stack.addSettings method to throw an error + const stackStub = sandbox.stub().rejects(new Error('Stack settings error')); + sandbox.stub(importStack, 'stack').value({ addSettings: stackStub }); + + const logSpy = sandbox.spy(console, 'log'); + + await importStack.start(); + + // The error should be caught and handled + expect(stackStub.called).to.be.true; + }); + + it('should handle null stackSettings', async () => { + // Create both required files + fs.mkdirSync(path.dirname((importStack as any).stackSettingsPath), { recursive: true }); + fs.mkdirSync(path.dirname((importStack as any).envUidMapperPath), { recursive: true }); + + fs.writeFileSync((importStack as any).stackSettingsPath, JSON.stringify(null)); + fs.writeFileSync((importStack as any).envUidMapperPath, JSON.stringify({ 'env1': 'new-env1' })); + + // Mock the stack.addSettings method + const stackStub = sandbox.stub().resolves(); + sandbox.stub(importStack, 'stack').value({ addSettings: stackStub }); + + const logSpy = sandbox.spy(console, 'log'); + + await importStack.start(); + + expect(stackStub.calledWith(null)).to.be.true; + }); + + it('should handle empty stackSettings object', async () => { + // Create both required files + fs.mkdirSync(path.dirname((importStack as any).stackSettingsPath), { recursive: true }); + fs.mkdirSync(path.dirname((importStack as any).envUidMapperPath), { recursive: true }); + + fs.writeFileSync((importStack as any).stackSettingsPath, JSON.stringify({})); + fs.writeFileSync((importStack as any).envUidMapperPath, JSON.stringify({ 'env1': 'new-env1' })); + + // Mock the stack.addSettings method + const stackStub = sandbox.stub().resolves(); + sandbox.stub(importStack, 'stack').value({ addSettings: stackStub }); + + const logSpy = sandbox.spy(console, 'log'); + + await importStack.start(); + + expect(stackStub.calledWith({})).to.be.true; + }); + + it('should handle stackSettings without live_preview property', async () => { + // Create both required files + fs.mkdirSync(path.dirname((importStack as any).stackSettingsPath), { recursive: true }); + fs.mkdirSync(path.dirname((importStack as any).envUidMapperPath), { recursive: true }); + + const stackSettings = { + other: 'settings', + not_live_preview: 'value' + }; + + fs.writeFileSync((importStack as any).stackSettingsPath, JSON.stringify(stackSettings)); + fs.writeFileSync((importStack as any).envUidMapperPath, JSON.stringify({ 'env1': 'new-env1' })); + + // Mock the stack.addSettings method + const stackStub = sandbox.stub().resolves(); + sandbox.stub(importStack, 'stack').value({ addSettings: stackStub }); + + const logSpy = sandbox.spy(console, 'log'); + + await importStack.start(); + + expect(stackStub.calledWith(stackSettings)).to.be.true; + }); + + it('should handle live_preview with null default-env', async () => { + // Create both required files + fs.mkdirSync(path.dirname((importStack as any).stackSettingsPath), { recursive: true }); + fs.mkdirSync(path.dirname((importStack as any).envUidMapperPath), { recursive: true }); + + const stackSettings = { + live_preview: { + 'default-env': null as any, + other: 'settings' + } + }; + + fs.writeFileSync((importStack as any).stackSettingsPath, JSON.stringify(stackSettings)); + fs.writeFileSync((importStack as any).envUidMapperPath, JSON.stringify({ 'env1': 'new-env1' })); + + // Mock the stack.addSettings method + const stackStub = sandbox.stub().resolves(); + sandbox.stub(importStack, 'stack').value({ addSettings: stackStub }); + + const logSpy = sandbox.spy(console, 'log'); + + await importStack.start(); + + const expectedSettings = { + live_preview: { + 'default-env': null as any, + other: 'settings' + } + }; + expect(stackStub.calledWith(expectedSettings)).to.be.true; + }); + + it('should handle live_preview with undefined default-env', async () => { + // Create both required files + fs.mkdirSync(path.dirname((importStack as any).stackSettingsPath), { recursive: true }); + fs.mkdirSync(path.dirname((importStack as any).envUidMapperPath), { recursive: true }); + + const stackSettings = { + live_preview: { + other: 'settings' + } + }; + + fs.writeFileSync((importStack as any).stackSettingsPath, JSON.stringify(stackSettings)); + fs.writeFileSync((importStack as any).envUidMapperPath, JSON.stringify({ 'env1': 'new-env1' })); + + // Mock the stack.addSettings method + const stackStub = sandbox.stub().resolves(); + sandbox.stub(importStack, 'stack').value({ addSettings: stackStub }); + + const logSpy = sandbox.spy(console, 'log'); + + await importStack.start(); + + expect(stackStub.calledWith(stackSettings)).to.be.true; + }); + + it('should handle live_preview with empty string default-env', async () => { + // Create both required files + fs.mkdirSync(path.dirname((importStack as any).stackSettingsPath), { recursive: true }); + fs.mkdirSync(path.dirname((importStack as any).envUidMapperPath), { recursive: true }); + + const stackSettings = { + live_preview: { + 'default-env': '' as any, + other: 'settings' + } + }; + + fs.writeFileSync((importStack as any).stackSettingsPath, JSON.stringify(stackSettings)); + fs.writeFileSync((importStack as any).envUidMapperPath, JSON.stringify({ '': 'new-env-uid' })); + + // Mock the stack.addSettings method + const stackStub = sandbox.stub().resolves(); + sandbox.stub(importStack, 'stack').value({ addSettings: stackStub }); + + const logSpy = sandbox.spy(console, 'log'); + + await importStack.start(); + + const expectedSettings = { + live_preview: { + 'default-env': '' as any, + other: 'settings' + } + }; + expect(stackStub.calledWith(expectedSettings)).to.be.true; + }); + + it('should handle malformed JSON in stack settings file', async () => { + // Create both required files + fs.mkdirSync(path.dirname((importStack as any).stackSettingsPath), { recursive: true }); + fs.mkdirSync(path.dirname((importStack as any).envUidMapperPath), { recursive: true }); + + fs.writeFileSync((importStack as any).stackSettingsPath, 'invalid json'); + fs.writeFileSync((importStack as any).envUidMapperPath, JSON.stringify({ 'env1': 'new-env1' })); + + const logSpy = sandbox.spy(console, 'log'); + + try { + await importStack.start(); + } catch (error) { + // Should handle JSON parse error gracefully + expect(error).to.be.instanceOf(Error); + } + }); + + it('should handle malformed JSON in env mapper file', async () => { + // Create both required files + fs.mkdirSync(path.dirname((importStack as any).stackSettingsPath), { recursive: true }); + fs.mkdirSync(path.dirname((importStack as any).envUidMapperPath), { recursive: true }); + + fs.writeFileSync((importStack as any).stackSettingsPath, JSON.stringify({ some: 'settings' })); + fs.writeFileSync((importStack as any).envUidMapperPath, 'invalid json'); + + const logSpy = sandbox.spy(console, 'log'); + + try { + await importStack.start(); + } catch (error) { + // Should handle JSON parse error gracefully + expect(error).to.be.instanceOf(Error); + } + }); + + it('should handle file read errors', async () => { + // Create directories but make files unreadable + fs.mkdirSync(path.dirname((importStack as any).stackSettingsPath), { recursive: true }); + fs.mkdirSync(path.dirname((importStack as any).envUidMapperPath), { recursive: true }); + + fs.writeFileSync((importStack as any).stackSettingsPath, JSON.stringify({ some: 'settings' })); + fs.writeFileSync((importStack as any).envUidMapperPath, JSON.stringify({ 'env1': 'new-env1' })); + + // Make file unreadable + fs.chmodSync((importStack as any).stackSettingsPath, 0o000); + + const logSpy = sandbox.spy(console, 'log'); + + try { + await importStack.start(); + } catch (error) { + // Should handle file read error gracefully + expect(error).to.be.instanceOf(Error); + } finally { + // Restore file permissions for cleanup + try { + fs.chmodSync((importStack as any).stackSettingsPath, 0o644); + } catch (e) { + // Ignore + } + } + }); + }); +}); \ No newline at end of file