Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: filename generation and match timestamp format on utils-py #18

Merged
merged 2 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/code/functions/writeAVMDebugTrace.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@ console.log(`Trace content: ${result.traceContent}`);

## Defined in

[debugging/writeAVMDebugTrace.ts:20](https://github.com/algorandfoundation/algokit-utils-ts-debug/blob/main/src/debugging/writeAVMDebugTrace.ts#L20)
[debugging/writeAVMDebugTrace.ts:65](https://github.com/algorandfoundation/algokit-utils-ts-debug/blob/main/src/debugging/writeAVMDebugTrace.ts#L65)
46 changes: 45 additions & 1 deletion src/debugging/writeAVMDebugTrace.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ import { Config, EventType, performAtomicTransactionComposerSimulate } from '@al
import { algorandFixture } from '@algorandfoundation/algokit-utils/testing'
import { describe, expect, test } from '@jest/globals'
import algosdk, { makeEmptyTransactionSigner } from 'algosdk'
import { SimulateResponse } from 'algosdk/dist/types/client/v2/algod/models/types'
import * as fs from 'fs/promises'
import * as os from 'os'
import * as path from 'path'
import { DEBUG_TRACES_DIR } from '../constants'
import { registerDebugEventHandlers } from '../index'
import { generateDebugTraceFilename } from './writeAVMDebugTrace'

describe('simulateAndPersistResponse tests', () => {
describe('writeAVMDebugTrace tests', () => {
const localnet = algorandFixture()

beforeAll(async () => {
Expand Down Expand Up @@ -45,3 +47,45 @@ describe('simulateAndPersistResponse tests', () => {
jest.restoreAllMocks()
})
})

describe('generateDebugTraceFilename', () => {
const TEST_CASES: Array<[string, object, string]> = [
[
'single payment transaction',
{
lastRound: 1000,
txnGroups: [
{
txnResults: [{ txnResult: { txn: { txn: { type: 'pay' } } } }],
},
],
},
'1pay',
],
[
'multiple transaction types',
{
lastRound: 1000,
txnGroups: [
{
txnResults: [
{ txnResult: { txn: { txn: { type: 'pay' } } } },
{ txnResult: { txn: { txn: { type: 'pay' } } } },
{ txnResult: { txn: { txn: { type: 'axfer' } } } },
{ txnResult: { txn: { txn: { type: 'appl' } } } },
{ txnResult: { txn: { txn: { type: 'appl' } } } },
{ txnResult: { txn: { txn: { type: 'appl' } } } },
],
},
],
},
'2pay_1axfer_3appl',
],
]

test.each(TEST_CASES)('%s', (testName, mockResponse, expectedPattern) => {
const timestamp = '20230101_120000'
const filename = generateDebugTraceFilename(mockResponse as SimulateResponse, timestamp)
expect(filename).toBe(`${timestamp}_lr${(mockResponse as SimulateResponse).lastRound}_${expectedPattern}.trace.avm.json`)
})
})
62 changes: 48 additions & 14 deletions src/debugging/writeAVMDebugTrace.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,52 @@
import { AVMTracesEventData } from '@algorandfoundation/algokit-utils'
import { SimulateResponse } from 'algosdk/dist/types/client/v2/algod/models/types'
import { DEBUG_TRACES_DIR } from '../constants'
import { getProjectRoot, joinPaths, writeToFile } from '../utils'

type TxnTypeCount = {
type: string
count: number
}

/**
* Formats a date to YYYYMMDD_HHMMSS in UTC, equivalent to algokit-utils-py format:
* datetime.now(tz=timezone.utc).strftime("%Y%m%d_%H%M%S")
*/
export function formatTimestampUTC(date: Date): string {
// Get UTC components
const year = date.getUTCFullYear()
const month = String(date.getUTCMonth() + 1).padStart(2, '0') // Months are zero-based
const day = String(date.getUTCDate()).padStart(2, '0')
const hours = String(date.getUTCHours()).padStart(2, '0')
const minutes = String(date.getUTCMinutes()).padStart(2, '0')
const seconds = String(date.getUTCSeconds()).padStart(2, '0')

// Format the datetime string
return `${year}${month}${day}_${hours}${minutes}${seconds}`
}

export function generateDebugTraceFilename(simulateResponse: SimulateResponse, timestamp: string): string {
const txnGroups = simulateResponse.txnGroups
const txnTypesCount = txnGroups.reduce((acc: Map<string, TxnTypeCount>, txnGroup) => {
txnGroup.txnResults.forEach(({ txnResult }) => {
const { type } = txnResult.txn.txn
if (!acc.has(type)) {
acc.set(type, { type, count: 0 })
}
const entry = acc.get(type)!
entry.count++
})
return acc
}, new Map())

const txnTypesStr = Array.from(txnTypesCount.values())
.map(({ count, type }) => `${count}${type}`)
.join('_')

const lastRound = simulateResponse.lastRound
return `${timestamp}_lr${lastRound}_${txnTypesStr}.trace.avm.json`
}

/**
* Generates an AVM debug trace from the provided simulation response and persists it to a file.
*
Expand All @@ -20,22 +65,11 @@ import { getProjectRoot, joinPaths, writeToFile } from '../utils'
export async function writeAVMDebugTrace(input: AVMTracesEventData): Promise<void> {
try {
const simulateResponse = input.simulateResponse
const txnGroups = simulateResponse.txnGroups
const projectRoot = await getProjectRoot()

const txnTypesCount = txnGroups.reduce((acc: Record<string, number>, txnGroup) => {
const txnType = txnGroup.txnResults[0].txnResult.txn.txn.type
acc[txnType] = (acc[txnType] || 0) + 1
return acc
}, {})

const txnTypesStr = Object.entries(txnTypesCount)
.map(([type, count]) => `${count}${type}`)
.join('_')

const timestamp = new Date().toISOString().replace(/[:.]/g, '')
const timestamp = formatTimestampUTC(new Date())
const outputRootDir = joinPaths(projectRoot, DEBUG_TRACES_DIR)
const outputFilePath = joinPaths(outputRootDir, `${timestamp}_${txnTypesStr}.trace.avm.json`)
const filename = generateDebugTraceFilename(simulateResponse, timestamp)
const outputFilePath = joinPaths(outputRootDir, filename)

await writeToFile(outputFilePath, JSON.stringify(simulateResponse.get_obj_for_encoding(), null, 2))
} catch (error) {
Expand Down
Loading