From 03c273b19a72acda30223d321a2ddd4c6d062d7e Mon Sep 17 00:00:00 2001 From: gonzaloriestra <14979109+gonzaloriestra@users.noreply.github.com> Date: Fri, 5 Jun 2026 00:49:41 +0000 Subject: [PATCH] [Tests] Remove filesystem mocks in app-logs-polling.test.ts This PR refactors `packages/app/src/cli/services/dev/processes/app-logs-polling.test.ts` to replace `vi.mock('@shopify/cli-kit/node/fs')` with real filesystem operations using the `inTemporaryDirectory` utility. Key changes: - Removed global filesystem mock. - Wrapped each test case in `inTemporaryDirectory`. - Updated `testAppWithConfig` and `testFunctionExtension` to use paths within the temporary directory. - Replaced `mkdir` mock assertions with `fileExists` checks on the real filesystem. - Improved test isolation by moving setup from `beforeEach` to individual test blocks. - Fixed linting and type-check issues introduced during the refactor. --- .../dev/processes/app-logs-polling.test.ts | 316 ++++++++++-------- 1 file changed, 172 insertions(+), 144 deletions(-) diff --git a/packages/app/src/cli/services/dev/processes/app-logs-polling.test.ts b/packages/app/src/cli/services/dev/processes/app-logs-polling.test.ts index 0a570cb4cc9..78b741838e7 100644 --- a/packages/app/src/cli/services/dev/processes/app-logs-polling.test.ts +++ b/packages/app/src/cli/services/dev/processes/app-logs-polling.test.ts @@ -5,15 +5,15 @@ import { testFunctionExtension, } from '../../../models/app/app.test-data.js' import {pollAppLogs} from '../../app-logs/dev/poll-app-logs.js' -import {DeveloperPlatformClient} from '../../../utilities/developer-platform-client.js' import * as appLogsUtils from '../../app-logs/utils.js' import {AppEventWatcher} from '../app-events/app-event-watcher.js' import {AbortSignal} from '@shopify/cli-kit/node/abort' -import {mkdir} from '@shopify/cli-kit/node/fs' +import {inTemporaryDirectory, fileExists} from '@shopify/cli-kit/node/fs' +import {joinPath} from '@shopify/cli-kit/node/path' import {outputDebug} from '@shopify/cli-kit/node/output' -import {describe, expect, vi, Mock, beforeEach, test} from 'vitest' +import {describe, expect, vi, test} from 'vitest' +import {Writable} from 'stream' -vi.mock('@shopify/cli-kit/node/fs') vi.mock('@shopify/cli-kit/node/output') vi.mock('../../app-logs/dev/poll-app-logs.js') @@ -32,36 +32,44 @@ const DEFAULT_FUNCTION_CONFIG = { describe('app-logs-polling', () => { describe('setupAppLogsPollingProcess', () => { test('returns process metadata', async () => { - // Given - const developerPlatformClient = testDeveloperPlatformClient() - const session = await developerPlatformClient.session() - const localApp = testAppWithConfig() - const appWatcher = new AppEventWatcher(localApp) - - // When - const result = await setupAppLogsPollingProcess({ - developerPlatformClient, - subscription: {shopIds: SHOP_IDS, apiKey: API_KEY}, - storeName: 'storeName', - organizationId: 'organizationId', - localApp, - appWatcher, - }) + await inTemporaryDirectory(async (tmpDir) => { + // Given + const developerPlatformClient = testDeveloperPlatformClient() + const session = await developerPlatformClient.session() + const localApp = testAppWithConfig({ + app: { + directory: tmpDir, + configPath: joinPath(tmpDir, 'shopify.app.toml'), + }, + config: {}, + }) + const appWatcher = new AppEventWatcher(localApp) - // Then - expect(result).toMatchObject({ - type: 'app-logs-subscribe', - prefix: 'app-logs', - function: subscribeAndStartPolling, - options: { + // When + const result = await setupAppLogsPollingProcess({ developerPlatformClient, - appLogsSubscribeVariables: { - shopIds: SHOP_IDS, - apiKey: API_KEY, - }, + subscription: {shopIds: SHOP_IDS, apiKey: API_KEY}, + storeName: 'storeName', + organizationId: 'organizationId', localApp, appWatcher, - }, + }) + + // Then + expect(result).toMatchObject({ + type: 'app-logs-subscribe', + prefix: 'app-logs', + function: subscribeAndStartPolling, + options: { + developerPlatformClient, + appLogsSubscribeVariables: { + shopIds: SHOP_IDS, + apiKey: API_KEY, + }, + localApp, + appWatcher, + }, + }) }) }) }) @@ -73,129 +81,149 @@ describe('app-logs-polling', () => { token: TOKEN, } - let subscribeToAppLogs: Mock - let developerPlatformClient: DeveloperPlatformClient - let stdout: any - let stderr: any - let abortSignal: AbortSignal - let localApp: any - let appWatcher: any - - beforeEach(async () => { - stdout = {write: vi.fn()} - stderr = {write: vi.fn()} - abortSignal = new AbortSignal() - subscribeToAppLogs = vi.fn() - - // Create function extension - localApp = testAppWithConfig({ - config: {}, - app: { - allExtensions: [await testFunctionExtension({config: DEFAULT_FUNCTION_CONFIG})], - }, - }) - - appWatcher = { - onEvent: vi.fn().mockReturnThis(), - onStart: vi.fn().mockReturnThis(), - } - - developerPlatformClient = testDeveloperPlatformClient({subscribeToAppLogs}) - - vi.mocked(mkdir).mockResolvedValue() - vi.mocked(pollAppLogs).mockResolvedValue() - vi.spyOn(appLogsUtils, 'subscribeToAppLogs').mockResolvedValue(JWT_TOKEN) - }) - test('sets up app log polling', async () => { - // Given - subscribeToAppLogs.mockResolvedValue({appLogsSubscribe: {jwtToken: JWT_TOKEN, success: true}}) - - // When - await subscribeAndStartPolling( - {stdout, stderr, abortSignal}, - { + await inTemporaryDirectory(async (tmpDir) => { + // Given + const stdout = {write: vi.fn()} as unknown as Writable + const stderr = {write: vi.fn()} as unknown as Writable + const abortSignal = new AbortSignal() + const subscribeToAppLogs = vi.fn() + + const localApp = testAppWithConfig({ + app: { + directory: tmpDir, + configPath: joinPath(tmpDir, 'shopify.app.toml'), + allExtensions: [await testFunctionExtension({dir: joinPath(tmpDir, 'extensions', 'my-function')})], + }, + config: {}, + }) + + const onEvent = vi.fn().mockReturnThis() + const onStart = vi.fn().mockReturnThis() + const appWatcher = { + onEvent, + onStart, + } as unknown as AppEventWatcher + + const developerPlatformClient = testDeveloperPlatformClient({subscribeToAppLogs}) + + vi.mocked(pollAppLogs).mockResolvedValue() + vi.spyOn(appLogsUtils, 'subscribeToAppLogs').mockResolvedValue(JWT_TOKEN) + + subscribeToAppLogs.mockResolvedValue({appLogsSubscribe: {jwtToken: JWT_TOKEN, success: true}}) + + // When + await subscribeAndStartPolling( + {stdout, stderr, abortSignal}, + { + developerPlatformClient, + appLogsSubscribeVariables, + storeName: 'storeName', + organizationId: 'organizationId', + localApp, + appWatcher, + }, + ) + + expect(onStart).toHaveBeenCalledOnce() + expect(onEvent).toHaveBeenCalledOnce() + + const startCallback = onStart.mock.calls[0]![0] + const appEvent = { + app: localApp, + extensionEvents: [], + startTime: [0, 0], + path: '', + } + await startCallback(appEvent) + + // Then + expect(outputDebug).toHaveBeenCalledWith('Function extensions detected, starting logs polling') + expect(appLogsUtils.subscribeToAppLogs).toHaveBeenCalledWith( developerPlatformClient, appLogsSubscribeVariables, - storeName: 'storeName', - organizationId: 'organizationId', - localApp, - appWatcher, - }, - ) - - expect(appWatcher.onStart).toHaveBeenCalledOnce() - expect(appWatcher.onEvent).toHaveBeenCalledOnce() - - const startCallback = appWatcher.onStart.mock.calls[0][0] - const appEvent = { - app: localApp, - extensionEvents: [], - startTime: [0, 0], - path: '', - } - await startCallback(appEvent) - - // Then - expect(outputDebug).toHaveBeenCalledWith('Function extensions detected, starting logs polling') - expect(appLogsUtils.subscribeToAppLogs).toHaveBeenCalledWith( - developerPlatformClient, - appLogsSubscribeVariables, - 'organizationId', - stdout, - ) - expect(mkdir).toHaveBeenCalledWith(localApp.getLogsDir()) - expect(pollAppLogs).toHaveBeenCalledOnce() - expect(vi.mocked(pollAppLogs).mock.calls[0]?.[0]).toMatchObject({ - stdout, - appLogsFetchInput: {jwtToken: JWT_TOKEN}, - logsDir: localApp.getLogsDir(), + 'organizationId', + stdout, + ) + await expect(fileExists(localApp.getLogsDir())).resolves.toBe(true) + expect(pollAppLogs).toHaveBeenCalledOnce() + expect(vi.mocked(pollAppLogs).mock.calls[0]![0]).toMatchObject({ + stdout, + appLogsFetchInput: {jwtToken: JWT_TOKEN}, + logsDir: localApp.getLogsDir(), + }) + + const eventCallback = onEvent.mock.calls[0]![0] + expect(startCallback).toBe(eventCallback) }) - - const eventCallback = appWatcher.onEvent.mock.calls[0][0] - expect(startCallback).toBe(eventCallback) }) test('prints error and returns on query errors', async () => { - // Given - vi.spyOn(appLogsUtils, 'subscribeToAppLogs').mockImplementation(() => { - throw new Error('uh oh, another error') - }) - - // When - await subscribeAndStartPolling( - {stdout, stderr, abortSignal}, - { + await inTemporaryDirectory(async (tmpDir) => { + // Given + const stdout = {write: vi.fn()} as unknown as Writable + const stderr = {write: vi.fn()} as unknown as Writable + const abortSignal = new AbortSignal() + const subscribeToAppLogs = vi.fn() + + const localApp = testAppWithConfig({ + app: { + directory: tmpDir, + configPath: joinPath(tmpDir, 'shopify.app.toml'), + allExtensions: [await testFunctionExtension({dir: joinPath(tmpDir, 'extensions', 'my-function')})], + }, + config: {}, + }) + + const onEvent = vi.fn().mockReturnThis() + const onStart = vi.fn().mockReturnThis() + const appWatcher = { + onEvent, + onStart, + } as unknown as AppEventWatcher + + const developerPlatformClient = testDeveloperPlatformClient({subscribeToAppLogs}) + + vi.mocked(pollAppLogs).mockResolvedValue() + vi.spyOn(appLogsUtils, 'subscribeToAppLogs').mockImplementation(() => { + throw new Error('uh oh, another error') + }) + + // When + await subscribeAndStartPolling( + {stdout, stderr, abortSignal}, + { + developerPlatformClient, + appLogsSubscribeVariables, + storeName: 'storeName', + organizationId: 'organizationId', + localApp, + appWatcher, + }, + ) + + expect(onStart).toHaveBeenCalledOnce() + expect(onEvent).toHaveBeenCalledOnce() + + const startCallback = onStart.mock.calls[0]![0] + const appEvent = { + app: localApp, + extensionEvents: [], + startTime: [0, 0], + path: '', + } + await startCallback(appEvent) + + // Then + expect(appLogsUtils.subscribeToAppLogs).toHaveBeenCalledWith( developerPlatformClient, appLogsSubscribeVariables, - storeName: 'storeName', - organizationId: 'organizationId', - localApp, - appWatcher, - }, - ) - - expect(appWatcher.onStart).toHaveBeenCalledOnce() - expect(appWatcher.onEvent).toHaveBeenCalledOnce() - - const startCallback = appWatcher.onStart.mock.calls[0][0] - const appEvent = { - app: localApp, - extensionEvents: [], - startTime: [0, 0], - path: '', - } - await startCallback(appEvent) - - // Then - expect(appLogsUtils.subscribeToAppLogs).toHaveBeenCalledWith( - developerPlatformClient, - appLogsSubscribeVariables, - 'organizationId', - stdout, - ) - expect(outputDebug).toHaveBeenCalledWith('Failed to start function logs: Error: uh oh, another error', stderr) - expect(pollAppLogs).not.toHaveBeenCalled() + 'organizationId', + stdout, + ) + expect(outputDebug).toHaveBeenCalledWith('Failed to start function logs: Error: uh oh, another error', stderr) + expect(pollAppLogs).not.toHaveBeenCalled() + }) }) }) })