Skip to content
Open
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,4 @@ ENV/

.polytest*/
polytest_resources/
.algokit*/
.algokit-*
18 changes: 18 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Debug Vitest Test",
"runtimeExecutable": "npm",
"runtimeArgs": ["test", "--", "--run", "--no-coverage", "${file}"],
"cwd": "${fileDirname}/..",
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
}
]
}
7 changes: 6 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@
"polytest:validate-algod": "polytest --config test_configs/algod_client.jsonc --git 'https://github.com/algorandfoundation/algokit-polytest#main' validate -t vitest",
"polytest:generate-algod": "polytest --config test_configs/algod_client.jsonc --git 'https://github.com/algorandfoundation/algokit-polytest#main' generate -t vitest",
"polytest:start-mock-servers": "cd .polytest_algokit-polytest/resources/mock-server/scripts/ && ./start_all_servers.sh && cd ../../../..",
"polytest:stop-mock-servers": "cd .polytest_algokit-polytest/resources/mock-server/scripts/ && ./stop_all_servers.sh && cd ../../../.."
"polytest:stop-mock-servers": "cd .polytest_algokit-polytest/resources/mock-server/scripts/ && ./stop_all_servers.sh && cd ../../../..",
"generate:schemas": "npm run generate:schema --workspaces --if-present"
},
"overrides": {
"esbuild": "0.25.0"
Expand Down
10 changes: 8 additions & 2 deletions packages/kmd_client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,15 @@
"check-types": "tsc --noEmit",
"audit": "better-npm-audit audit",
"format": "prettier --config ../../.prettierrc.cjs --ignore-path ../../.prettierignore --write .",
"pre-commit": "run-s check-types lint:fix audit format test"
"pre-commit": "run-s check-types lint:fix audit format test",
"generate:schema": "tsx ../../scripts/generate-zod-schemas.ts --spec ../../.algokit-oas-generator/specs/kmd.oas3.json --output ./tests/schemas.ts"
},
"dependencies": {},
"peerDependencies": {},
"devDependencies": {}
"devDependencies": {
"@algorandfoundation/algokit-algod-client": "*",
"@algorandfoundation/algokit-common": "*",
"@algorandfoundation/algokit-transact": "*",
"zod": "^3.23.8"
}
}
28 changes: 28 additions & 0 deletions packages/kmd_client/tests/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,37 @@ function getMockServerUrl(): string {
return process.env.MOCK_KMD_URL || process.env.MOCK_KMD_SERVER || `http://127.0.0.1:${MOCK_PORTS.kmd.host}`
}

// Mock server configuration
export const config: ClientConfig = {
baseUrl: getMockServerUrl(),
token: process.env.MOCK_KMD_TOKEN || DEFAULT_TOKEN,
}

// Localnet KMD configuration
const kmdServer = process.env.KMD_SERVER || 'http://localhost'
const kmdPort = process.env.KMD_PORT || '4002'
const kmdBaseUrl = `${kmdServer}:${kmdPort}`

export const localnetConfig: ClientConfig = {
baseUrl: kmdBaseUrl,
token: process.env.KMD_TOKEN || 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
}

// Localnet Algod configuration
const algodServer = process.env.ALGOD_SERVER || 'http://localhost'
const algodPort = process.env.ALGOD_PORT || '4001'
const algodBaseUrl = `${algodServer}:${algodPort}`

export const localnetAlgodConfig = {
baseUrl: algodBaseUrl,
token: process.env.ALGOD_TOKEN || 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
}

// Test constants
export const TEST_WALLET_PASSWORD = 'test-password-123'
export const TEST_WALLET_DRIVER = 'sqlite'
export const MULTISIG_VERSION = 1
export const MULTISIG_THRESHOLD = 2
export const MULTISIG_KEY_COUNT = 3

export { TEST_ADDRESS, TEST_APP_ID, TEST_APP_ID_WITH_BOXES, TEST_BOX_NAME, TEST_ASSET_ID, TEST_TXID, TEST_ROUND }
42 changes: 42 additions & 0 deletions packages/kmd_client/tests/delete_v1_key.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Address } from '@algorandfoundation/algokit-common'
import { describe, expect, test } from 'vitest'
import { KmdClient } from '../src/client'
import { localnetConfig, TEST_WALLET_PASSWORD } from './config'
import { generateTestKey, getWalletHandle, releaseWalletHandle } from './fixtures'

describe('DELETE v1_key', () => {
// Polytest Suite: DELETE v1_key

describe('Common Tests', () => {
// Polytest Group: Common Tests

test('Basic request and response validation', async () => {
const client = new KmdClient(localnetConfig)
const { walletHandleToken } = await getWalletHandle(client)

try {
// Generate a key to delete
const addressStr = await generateTestKey(client, walletHandleToken)

// Verify key exists
const listBefore = await client.listKeysInWallet({ walletHandleToken })
expect(listBefore.addresses.map((a) => a.toString())).toContain(addressStr)

// Delete the key (returns void)
await expect(
client.deleteKey({
address: Address.fromString(addressStr),
walletHandleToken,
walletPassword: TEST_WALLET_PASSWORD,
}),
).resolves.toBeUndefined()

// Verify key was deleted
const listAfter = await client.listKeysInWallet({ walletHandleToken })
expect(listAfter.addresses.map((a) => a.toString())).not.toContain(addressStr)
} finally {
await releaseWalletHandle(client, walletHandleToken)
}
})
})
})
42 changes: 42 additions & 0 deletions packages/kmd_client/tests/delete_v1_multisig.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Address } from '@algorandfoundation/algokit-common'
import { describe, expect, test } from 'vitest'
import { KmdClient } from '../src/client'
import { localnetConfig, TEST_WALLET_PASSWORD } from './config'
import { createTestMultisig, getWalletHandle, releaseWalletHandle } from './fixtures'

describe('DELETE v1_multisig', () => {
// Polytest Suite: DELETE v1_multisig

describe('Common Tests', () => {
// Polytest Group: Common Tests

test('Basic request and response validation', async () => {
const client = new KmdClient(localnetConfig)
const { walletHandleToken } = await getWalletHandle(client)

try {
// Create a multisig first
const { multisigAddress } = await createTestMultisig(client, walletHandleToken)

// Verify multisig exists
const listBefore = await client.listMultisig({ walletHandleToken })
expect(listBefore.addresses.map((a) => a.toString())).toContain(multisigAddress)

// Delete the multisig (returns void)
await expect(
client.deleteMultisig({
address: Address.fromString(multisigAddress),
walletHandleToken,
walletPassword: TEST_WALLET_PASSWORD,
}),
).resolves.toBeUndefined()

// Verify multisig was deleted
const listAfter = await client.listMultisig({ walletHandleToken })
expect(listAfter.addresses.map((a) => a.toString())).not.toContain(multisigAddress)
} finally {
await releaseWalletHandle(client, walletHandleToken)
}
})
})
})
144 changes: 144 additions & 0 deletions packages/kmd_client/tests/fixtures.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { decodeAddress, encodeAddress } from '@algorandfoundation/algokit-common'
import type { KmdClient } from '../src/client'
import { MULTISIG_KEY_COUNT, MULTISIG_THRESHOLD, MULTISIG_VERSION, TEST_WALLET_DRIVER, TEST_WALLET_PASSWORD } from './config'

/**
* Generates a unique wallet name for testing
*/
export function generateWalletName(): string {
return `test-wallet-${Date.now()}-${Math.random().toString(36).substring(7)}`
}

/**
* Creates a test wallet and returns wallet ID and name
*/
export async function createTestWallet(
client: KmdClient,
password: string = TEST_WALLET_PASSWORD,
): Promise<{ walletId: string; walletName: string }> {
const walletName = generateWalletName()
const result = await client.createWallet({
walletName,
walletPassword: password,
walletDriverName: TEST_WALLET_DRIVER,
})
return {
walletId: result.wallet.id,
walletName: result.wallet.name,
}
}

/**
* Creates a wallet and initializes a wallet handle token (unlocks wallet)
*/
export async function getWalletHandle(
client: KmdClient,
password: string = TEST_WALLET_PASSWORD,
): Promise<{
walletHandleToken: string
walletId: string
walletName: string
}> {
// Create wallet
const { walletId, walletName } = await createTestWallet(client, password)

// Initialize handle (unlock)
const initResult = await client.initWalletHandle({
walletId,
walletPassword: password,
})

return {
walletHandleToken: initResult.walletHandleToken,
walletId,
walletName,
}
}

/**
* Releases a wallet handle token (locks wallet)
* Used for cleanup in tests
*/
export async function releaseWalletHandle(client: KmdClient, walletHandleToken: string): Promise<void> {
try {
await client.releaseWalletHandleToken({ walletHandleToken })
} catch (error) {
// Ignore errors during cleanup (handle may have already expired)
console.warn('Failed to release wallet handle:', error)
}
}

/**
* Generates a key in the wallet and returns the address
*/
export async function generateTestKey(client: KmdClient, walletHandleToken: string): Promise<string> {
const result = await client.generateKey({
walletHandleToken,
})
return result.address.toString()
}

/**
* Generates multiple keys for multisig tests
*/
export async function generateMultipleKeys(
client: KmdClient,
walletHandleToken: string,
count: number = MULTISIG_KEY_COUNT,
): Promise<string[]> {
const addresses: string[] = []
for (let i = 0; i < count; i++) {
const address = await generateTestKey(client, walletHandleToken)
addresses.push(address)
}
return addresses
}

/**
* Converts an Algorand address string to a public key (Uint8Array)
*/
export function addressToPublicKey(address: string): Uint8Array {
return decodeAddress(address).publicKey
}

/**
* Converts a public key (Uint8Array) to an Algorand address string
*/
export function publicKeyToAddress(publicKey: Uint8Array): string {
return encodeAddress(publicKey)
}

/**
* Creates a multisig account with test keys
*/
export async function createTestMultisig(
client: KmdClient,
walletHandleToken: string,
threshold: number = MULTISIG_THRESHOLD,
keyCount: number = MULTISIG_KEY_COUNT,
): Promise<{
multisigAddress: string
publicKeys: Uint8Array[]
addresses: string[]
threshold: number
}> {
// Generate keys
const addresses = await generateMultipleKeys(client, walletHandleToken, keyCount)

const publicKeys = addresses.map((addr) => addressToPublicKey(addr))

// Import multisig
const result = await client.importMultisig({
walletHandleToken,
multisigVersion: MULTISIG_VERSION,
threshold: threshold,
publicKeys: publicKeys,
})

return {
multisigAddress: result.address.toString(),
publicKeys,
addresses,
threshold,
}
}
20 changes: 20 additions & 0 deletions packages/kmd_client/tests/get_v1_wallets.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { describe, test } from 'vitest'
import { KmdClient } from '../src/client'
import { localnetConfig } from './config'
import { ListWalletsResponse } from './schemas'

describe('GET v1_wallets', () => {
// Polytest Suite: GET v1_wallets

describe('Common Tests', () => {
// Polytest Group: Common Tests

test('Basic request and response validation', async () => {
const client = new KmdClient(localnetConfig)

const result = await client.listWallets()

ListWalletsResponse.parse(result)
})
})
})
20 changes: 20 additions & 0 deletions packages/kmd_client/tests/get_versions.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { describe, test } from 'vitest'
import { KmdClient } from '../src/client'
import { localnetConfig } from './config'
import { VersionsResponse } from './schemas'

describe('GET versions', () => {
// Polytest Suite: GET versions

describe('Common Tests', () => {
// Polytest Group: Common Tests

test('Basic request and response validation', async () => {
const client = new KmdClient(localnetConfig)

const result = await client.getVersion()

Check failure on line 15 in packages/kmd_client/tests/get_versions.test.ts

View workflow job for this annotation

GitHub Actions / build-and-test

Property 'getVersion' does not exist on type 'KmdClient'. Did you mean 'version'?

VersionsResponse.parse(result)
})
})
})
Loading
Loading