diff --git a/packages/app/src/cli/services/app-logs/logs-command/ui/components/Logs.test.tsx b/packages/app/src/cli/services/app-logs/logs-command/ui/components/Logs.test.tsx index ea9db1729c..aac659ace7 100644 --- a/packages/app/src/cli/services/app-logs/logs-command/ui/components/Logs.test.tsx +++ b/packages/app/src/cli/services/app-logs/logs-command/ui/components/Logs.test.tsx @@ -45,117 +45,6 @@ const NETWORK_ACCESS_HTTP_RESPONSE = { }, } -const USE_POLL_APP_LOGS_RETURN_VALUE = { - appLogOutputs: [ - { - appLog: new FunctionRunLog({ - export: 'run', - input: INPUT, - inputBytes: INPUT_BYTES, - output: OUTPUT, - outputBytes: OUTPUT_BYTES, - logs: LOGS, - functionId: FUNCTION_ID, - fuelConsumed: FUEL_CONSUMED, - errorMessage: 'errorMessage', - errorType: 'errorType', - inputQueryVariablesMetafieldValue: '{"key":"value"}', - inputQueryVariablesMetafieldNamespace: 'inputQueryVariablesMetafieldNamespace', - inputQueryVariablesMetafieldKey: 'inputQueryVariablesMetafieldKey', - }), - prefix: { - functionId: FUNCTION_ID, - logTimestamp: TIME, - description: `export "run" executed in 0.5124M instructions`, - storeName: 'my-store', - status: STATUS === 'success' ? 'Success' : 'Failure', - source: SOURCE, - }, - }, - { - appLog: new NetworkAccessResponseFromCacheLog({ - cacheEntryEpochMs: 1683904621000, - cacheTtlMs: 300000, - httpRequest: NETWORK_ACCESS_HTTP_REQUEST, - httpResponse: NETWORK_ACCESS_HTTP_RESPONSE, - }), - prefix: { - functionId: FUNCTION_ID, - logTimestamp: TIME, - description: 'network access response from cache', - storeName: 'my-store', - status: 'Success', - source: SOURCE, - }, - }, - { - appLog: new NetworkAccessRequestExecutedLog({ - attempt: 1, - connectTimeMs: 40, - writeReadTimeMs: 40, - httpRequest: NETWORK_ACCESS_HTTP_REQUEST, - httpResponse: NETWORK_ACCESS_HTTP_RESPONSE, - error: null, - }), - prefix: { - functionId: FUNCTION_ID, - logTimestamp: TIME, - description: 'network access request executed in 80 ms', - status: 'Success', - storeName: 'my-store', - source: SOURCE, - }, - }, - { - appLog: new NetworkAccessRequestExecutedLog({ - attempt: 1, - connectTimeMs: null, - writeReadTimeMs: null, - httpRequest: NETWORK_ACCESS_HTTP_REQUEST, - httpResponse: null, - error: 'Timeout Error', - }), - prefix: { - functionId: FUNCTION_ID, - logTimestamp: TIME, - description: 'network access request executed', - storeName: 'my-store', - status: 'Failure', - source: SOURCE, - }, - }, - { - appLog: new NetworkAccessRequestExecutionInBackgroundLog({ - reason: BackgroundExecutionReason.NoCachedResponse, - httpRequest: NETWORK_ACCESS_HTTP_REQUEST, - }), - prefix: { - functionId: FUNCTION_ID, - logTimestamp: TIME, - description: 'network access request executing in background', - storeName: 'my-store', - status: 'Success', - source: SOURCE, - }, - }, - { - appLog: new NetworkAccessRequestExecutionInBackgroundLog({ - reason: BackgroundExecutionReason.CacheAboutToExpire, - httpRequest: NETWORK_ACCESS_HTTP_REQUEST, - }), - prefix: { - functionId: FUNCTION_ID, - logTimestamp: TIME, - description: 'network access request executing in background', - storeName: 'my-store', - status: 'Success', - source: SOURCE, - }, - }, - ], - errors: [], -} - const USE_POLL_APP_LOGS_ERRORS_RETURN_VALUE = { errors: ['Test Error'], appLogOutputs: [], @@ -167,129 +56,471 @@ STORE_NAME_BY_ID.set('1', 'my-store') const EMPTY_FILTERS = {status: undefined, sources: undefined} describe('Logs', () => { - test('renders prefix and applogs', async () => { - // Given - const mockedUsePollAppLogs = vi.fn().mockReturnValue(USE_POLL_APP_LOGS_RETURN_VALUE) - vi.mocked(usePollAppLogs).mockImplementation(mockedUsePollAppLogs) - // When - const renderInstance = render( - , - ) + describe('App Logs', () => { + test('renders FunctionRunLog correctly with metafield data', async () => { + const appLogOutput = { + appLog: new FunctionRunLog({ + export: 'run', + input: INPUT, + inputBytes: INPUT_BYTES, + output: OUTPUT, + outputBytes: OUTPUT_BYTES, + logs: LOGS, + functionId: FUNCTION_ID, + fuelConsumed: FUEL_CONSUMED, + errorMessage: 'errorMessage', + errorType: 'errorType', + inputQueryVariablesMetafieldValue: '{"key":"value"}', + inputQueryVariablesMetafieldNamespace: 'inputQueryVariablesMetafieldNamespace', + inputQueryVariablesMetafieldKey: 'inputQueryVariablesMetafieldKey', + }), + prefix: { + functionId: FUNCTION_ID, + logTimestamp: TIME, + description: `export "run" executed in 0.5124M instructions`, + storeName: 'my-store', + status: STATUS === 'success' ? 'Success' : 'Failure', + source: SOURCE, + }, + } - // Then - const lastFrame = renderInstance.lastFrame() + const mockedUsePollAppLogs = vi.fn().mockReturnValue({appLogOutputs: [appLogOutput], errors: []}) + vi.mocked(usePollAppLogs).mockImplementation(mockedUsePollAppLogs) - expect(unstyled(lastFrame!)).toMatchInlineSnapshot(` - "2024-06-18 16:02:04.868 my-store my-function Success export \\"run\\" executed in 0.5124M instructions - test logs + const renderInstance = render( + , + ) - Input Query Variables: + const lastFrame = renderInstance.lastFrame() - Namespace: inputQueryVariablesMetafieldNamespace - Key: inputQueryVariablesMetafieldKey + expect(unstyled(lastFrame!)).toMatchInlineSnapshot(` + "2024-06-18 16:02:04.868 my-store my-function Success export \\"run\\" executed in 0.5124M instructions + test logs - { - \\"key\\": \\"value\\" - } + Input Query Variables: - Input (10 bytes): + Namespace: inputQueryVariablesMetafieldNamespace + Key: inputQueryVariablesMetafieldKey - { - \\"test\\": \\"input\\" - } + { + \\"key\\": \\"value\\" + } - Output (10 bytes): + Input (10 bytes): - { - \\"test\\": \\"output\\" - } - 2024-06-18 16:02:04.868 my-store my-function Success network access response from cache - Cache write time: 2023-05-12T15:17:01.000Z - Cache TTL: 300 s - HTTP request: - { - \\"url\\": \\"https://api.example.com/hello\\", - \\"method\\": \\"GET\\", - \\"headers\\": {}, - \\"body\\": null, - \\"policy\\": { - \\"read_timeout_ms\\": 500 - } - } - HTTP response: - { - \\"status\\": 200, - \\"body\\": \\"Success\\", - \\"headers\\": { - \\"header1\\": \\"value1\\" - } - } - 2024-06-18 16:02:04.868 my-store my-function Success network access request executed in 80 ms - Attempt: 1 - Connect time: 40 ms - Write read time: 40 ms - HTTP request: - { - \\"url\\": \\"https://api.example.com/hello\\", - \\"method\\": \\"GET\\", - \\"headers\\": {}, - \\"body\\": null, - \\"policy\\": { - \\"read_timeout_ms\\": 500 - } - } - HTTP response: - { - \\"status\\": 200, - \\"body\\": \\"Success\\", - \\"headers\\": { - \\"header1\\": \\"value1\\" - } - } - 2024-06-18 16:02:04.868 my-store my-function Failure network access request executed - Attempt: 1 - HTTP request: - { - \\"url\\": \\"https://api.example.com/hello\\", - \\"method\\": \\"GET\\", - \\"headers\\": {}, - \\"body\\": null, - \\"policy\\": { - \\"read_timeout_ms\\": 500 + { + \\"test\\": \\"input\\" + } + + Output (10 bytes): + + { + \\"test\\": \\"output\\" + }" + `) + + renderInstance.unmount() + }) + + test('renders FunctionRunLog correctly with nil metafield value', async () => { + const appLogOutput = { + appLog: new FunctionRunLog({ + export: 'run', + input: INPUT, + inputBytes: INPUT_BYTES, + output: OUTPUT, + outputBytes: OUTPUT_BYTES, + logs: LOGS, + functionId: FUNCTION_ID, + fuelConsumed: FUEL_CONSUMED, + errorMessage: 'errorMessage', + errorType: 'errorType', + inputQueryVariablesMetafieldValue: null, + inputQueryVariablesMetafieldNamespace: 'inputQueryVariablesMetafieldNamespace', + inputQueryVariablesMetafieldKey: 'inputQueryVariablesMetafieldKey', + }), + prefix: { + functionId: FUNCTION_ID, + logTimestamp: TIME, + description: `export "run" executed in 0.5124M instructions`, + storeName: 'my-store', + status: STATUS === 'success' ? 'Success' : 'Failure', + source: SOURCE, + }, + } + + const mockedUsePollAppLogs = vi.fn().mockReturnValue({appLogOutputs: [appLogOutput], errors: []}) + vi.mocked(usePollAppLogs).mockImplementation(mockedUsePollAppLogs) + + const renderInstance = render( + , + ) + + const lastFrame = renderInstance.lastFrame() + + expect(unstyled(lastFrame!)).toMatchInlineSnapshot(` + "2024-06-18 16:02:04.868 my-store my-function Success export \\"run\\" executed in 0.5124M instructions + test logs + + Input Query Variables: + + Namespace: inputQueryVariablesMetafieldNamespace + Key: inputQueryVariablesMetafieldKey + + No metafield data found! + + Input (10 bytes): + + { + \\"test\\": \\"input\\" + } + + Output (10 bytes): + + { + \\"test\\": \\"output\\" + }" + `) + + renderInstance.unmount() + }) + + test('redners FunctionRunLog without iqv when key and namespace are null', async () => { + const appLogOutput = { + appLog: new FunctionRunLog({ + export: 'run', + input: INPUT, + inputBytes: INPUT_BYTES, + output: OUTPUT, + outputBytes: OUTPUT_BYTES, + logs: LOGS, + functionId: FUNCTION_ID, + fuelConsumed: FUEL_CONSUMED, + errorMessage: 'errorMessage', + errorType: 'errorType', + inputQueryVariablesMetafieldValue: null, + inputQueryVariablesMetafieldNamespace: null, + inputQueryVariablesMetafieldKey: null, + }), + prefix: { + functionId: FUNCTION_ID, + logTimestamp: TIME, + description: `export "run" executed in 0.5124M instructions`, + storeName: 'my-store', + status: STATUS === 'success' ? 'Success' : 'Failure', + source: SOURCE, + }, + } + + const mockedUsePollAppLogs = vi.fn().mockReturnValue({appLogOutputs: [appLogOutput], errors: []}) + vi.mocked(usePollAppLogs).mockImplementation(mockedUsePollAppLogs) + + const renderInstance = render( + , + ) + + const lastFrame = renderInstance.lastFrame() + + expect(unstyled(lastFrame!)).toMatchInlineSnapshot(` + "2024-06-18 16:02:04.868 my-store my-function Success export \\"run\\" executed in 0.5124M instructions + test logs + + Input (10 bytes): + + { + \\"test\\": \\"input\\" + } + + Output (10 bytes): + + { + \\"test\\": \\"output\\" + }" + `) + + renderInstance.unmount() + }) + }) + + describe('Webhooks', () => { + test('renders NetworkAccessResponseFromCacheLog correctly', async () => { + const webhookLogOutput = { + appLog: new NetworkAccessResponseFromCacheLog({ + cacheEntryEpochMs: 1683904621000, + cacheTtlMs: 300000, + httpRequest: NETWORK_ACCESS_HTTP_REQUEST, + httpResponse: NETWORK_ACCESS_HTTP_RESPONSE, + }), + prefix: { + functionId: FUNCTION_ID, + logTimestamp: TIME, + description: 'network access response from cache', + storeName: 'my-store', + status: 'Success', + source: SOURCE, + }, + } + + const mockedUsePollAppLogs = vi.fn().mockReturnValue({appLogOutputs: [webhookLogOutput], errors: []}) + vi.mocked(usePollAppLogs).mockImplementation(mockedUsePollAppLogs) + + const renderInstance = render( + , + ) + + const lastFrame = renderInstance.lastFrame() + + expect(unstyled(lastFrame!)).toMatchInlineSnapshot(` + "2024-06-18 16:02:04.868 my-store my-function Success network access response from cache + Cache write time: 2023-05-12T15:17:01.000Z + Cache TTL: 300 s + HTTP request: + { + \\"url\\": \\"https://api.example.com/hello\\", + \\"method\\": \\"GET\\", + \\"headers\\": {}, + \\"body\\": null, + \\"policy\\": { + \\"read_timeout_ms\\": 500 + } } - } - Error: Timeout Error - 2024-06-18 16:02:04.868 my-store my-function Success network access request executing in background - Reason: No cached response available - HTTP request: - { - \\"url\\": \\"https://api.example.com/hello\\", - \\"method\\": \\"GET\\", - \\"headers\\": {}, - \\"body\\": null, - \\"policy\\": { - \\"read_timeout_ms\\": 500 + HTTP response: + { + \\"status\\": 200, + \\"body\\": \\"Success\\", + \\"headers\\": { + \\"header1\\": \\"value1\\" + } + }" + `) + + renderInstance.unmount() + }) + + test('renders NetworkAccessRequestExecutedLog correctly with success', async () => { + const webhookLogOutput = { + appLog: new NetworkAccessRequestExecutedLog({ + attempt: 1, + connectTimeMs: 40, + writeReadTimeMs: 40, + httpRequest: NETWORK_ACCESS_HTTP_REQUEST, + httpResponse: NETWORK_ACCESS_HTTP_RESPONSE, + error: null, + }), + prefix: { + functionId: FUNCTION_ID, + logTimestamp: TIME, + description: 'network access request executed in 80 ms', + status: 'Success', + storeName: 'my-store', + source: SOURCE, + }, + } + + const mockedUsePollAppLogs = vi.fn().mockReturnValue({appLogOutputs: [webhookLogOutput], errors: []}) + vi.mocked(usePollAppLogs).mockImplementation(mockedUsePollAppLogs) + + const renderInstance = render( + , + ) + + const lastFrame = renderInstance.lastFrame() + + expect(unstyled(lastFrame!)).toMatchInlineSnapshot(` + "2024-06-18 16:02:04.868 my-store my-function Success network access request executed in 80 ms + Attempt: 1 + Connect time: 40 ms + Write read time: 40 ms + HTTP request: + { + \\"url\\": \\"https://api.example.com/hello\\", + \\"method\\": \\"GET\\", + \\"headers\\": {}, + \\"body\\": null, + \\"policy\\": { + \\"read_timeout_ms\\": 500 + } } - } - 2024-06-18 16:02:04.868 my-store my-function Success network access request executing in background - Reason: Cache is about to expire - HTTP request: - { - \\"url\\": \\"https://api.example.com/hello\\", - \\"method\\": \\"GET\\", - \\"headers\\": {}, - \\"body\\": null, - \\"policy\\": { - \\"read_timeout_ms\\": 500 + HTTP response: + { + \\"status\\": 200, + \\"body\\": \\"Success\\", + \\"headers\\": { + \\"header1\\": \\"value1\\" + } + }" + `) + + renderInstance.unmount() + }) + + test('renders NetworkAccessRequestExecutedLog correctly with failure', async () => { + const webhookLogOutput = { + appLog: new NetworkAccessRequestExecutedLog({ + attempt: 1, + connectTimeMs: null, + writeReadTimeMs: null, + httpRequest: NETWORK_ACCESS_HTTP_REQUEST, + httpResponse: null, + error: 'Timeout Error', + }), + prefix: { + functionId: FUNCTION_ID, + logTimestamp: TIME, + description: 'network access request executed', + storeName: 'my-store', + status: 'Failure', + source: SOURCE, + }, + } + + const mockedUsePollAppLogs = vi.fn().mockReturnValue({appLogOutputs: [webhookLogOutput], errors: []}) + vi.mocked(usePollAppLogs).mockImplementation(mockedUsePollAppLogs) + + const renderInstance = render( + , + ) + + const lastFrame = renderInstance.lastFrame() + + expect(unstyled(lastFrame!)).toMatchInlineSnapshot(` + "2024-06-18 16:02:04.868 my-store my-function Failure network access request executed + Attempt: 1 + HTTP request: + { + \\"url\\": \\"https://api.example.com/hello\\", + \\"method\\": \\"GET\\", + \\"headers\\": {}, + \\"body\\": null, + \\"policy\\": { + \\"read_timeout_ms\\": 500 + } } - }" - `) + Error: Timeout Error" + `) - renderInstance.unmount() + renderInstance.unmount() + }) + + test('renders NetworkAccessRequestExecutionInBackgroundLog correctly with NoCachedResponse', async () => { + const webhookLogOutput = { + appLog: new NetworkAccessRequestExecutionInBackgroundLog({ + reason: BackgroundExecutionReason.NoCachedResponse, + httpRequest: NETWORK_ACCESS_HTTP_REQUEST, + }), + prefix: { + functionId: FUNCTION_ID, + logTimestamp: TIME, + description: 'network access request executing in background', + storeName: 'my-store', + status: 'Success', + source: SOURCE, + }, + } + + const mockedUsePollAppLogs = vi.fn().mockReturnValue({appLogOutputs: [webhookLogOutput], errors: []}) + vi.mocked(usePollAppLogs).mockImplementation(mockedUsePollAppLogs) + + const renderInstance = render( + , + ) + + const lastFrame = renderInstance.lastFrame() + + expect(unstyled(lastFrame!)).toMatchInlineSnapshot(` + "2024-06-18 16:02:04.868 my-store my-function Success network access request executing in background + Reason: No cached response available + HTTP request: + { + \\"url\\": \\"https://api.example.com/hello\\", + \\"method\\": \\"GET\\", + \\"headers\\": {}, + \\"body\\": null, + \\"policy\\": { + \\"read_timeout_ms\\": 500 + } + }" + `) + + renderInstance.unmount() + }) + + test('renders NetworkAccessRequestExecutionInBackgroundLog correctly with CacheAboutToExpire', async () => { + const webhookLogOutput = { + appLog: new NetworkAccessRequestExecutionInBackgroundLog({ + reason: BackgroundExecutionReason.CacheAboutToExpire, + httpRequest: NETWORK_ACCESS_HTTP_REQUEST, + }), + prefix: { + functionId: FUNCTION_ID, + logTimestamp: TIME, + description: 'network access request executing in background', + storeName: 'my-store', + status: 'Success', + source: SOURCE, + }, + } + + const mockedUsePollAppLogs = vi.fn().mockReturnValue({appLogOutputs: [webhookLogOutput], errors: []}) + vi.mocked(usePollAppLogs).mockImplementation(mockedUsePollAppLogs) + + const renderInstance = render( + , + ) + + const lastFrame = renderInstance.lastFrame() + + expect(unstyled(lastFrame!)).toMatchInlineSnapshot(` + "2024-06-18 16:02:04.868 my-store my-function Success network access request executing in background + Reason: Cache is about to expire + HTTP request: + { + \\"url\\": \\"https://api.example.com/hello\\", + \\"method\\": \\"GET\\", + \\"headers\\": {}, + \\"body\\": null, + \\"policy\\": { + \\"read_timeout_ms\\": 500 + } + }" + `) + + renderInstance.unmount() + }) + + // Add more tests for other webhook log types (NetworkAccessRequestExecutedLog, NetworkAccessRequestExecutionInBackgroundLog) }) test('handles errors', async () => {