-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d9bcdcb
commit 594c375
Showing
36 changed files
with
425 additions
and
123 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import { Explored } from '@siafoundation/explored-js' | ||
import { Hostd } from '@siafoundation/hostd-js' | ||
import { Bus } from '@siafoundation/renterd-js' | ||
import { Walletd } from '@siafoundation/walletd-js' | ||
import { clusterd, setupCluster } from '@siafoundation/clusterd' | ||
import { BrowserContext } from 'playwright' | ||
|
||
export type Cluster = Awaited<ReturnType<typeof startCluster>> | ||
|
||
export async function startCluster({ | ||
renterdCount = 1, | ||
walletdCount = 1, | ||
hostdCount = 3, | ||
context, | ||
}: { | ||
renterdCount?: number | ||
walletdCount?: number | ||
hostdCount?: number | ||
context: BrowserContext | ||
}) { | ||
await setupCluster({ | ||
exploredCount: 1, | ||
renterdCount, | ||
walletdCount, | ||
hostdCount, | ||
}) | ||
const explored = clusterd.nodes.find((n) => n.type === 'explored') | ||
const renterds = clusterd.nodes.filter((n) => n.type === 'renterd') | ||
const hostds = clusterd.nodes.filter((n) => n.type === 'hostd') | ||
const walletds = clusterd.nodes.filter((n) => n.type === 'walletd') | ||
if ( | ||
explored === undefined || | ||
renterds.length !== renterdCount || | ||
hostds.length !== hostdCount || | ||
walletds.length !== walletdCount | ||
) { | ||
throw new Error('Failed to start cluster') | ||
} | ||
const daemons = { | ||
explored: { | ||
node: explored, | ||
api: Explored({ | ||
api: `${explored.apiAddress}/api`, | ||
password: explored.password, | ||
}), | ||
}, | ||
renterds: renterds.map((r) => ({ | ||
node: r, | ||
api: Bus({ | ||
api: `${r.apiAddress}/api`, | ||
password: r.password, | ||
}), | ||
})), | ||
hostds: hostds.map((h) => ({ | ||
node: h, | ||
api: Hostd({ | ||
api: `${h.apiAddress}/api`, | ||
password: h.password, | ||
}), | ||
})), | ||
walletds: walletds.map((w) => ({ | ||
node: w, | ||
api: Walletd({ | ||
api: `${w.apiAddress}/api`, | ||
password: w.password, | ||
}), | ||
})), | ||
} | ||
console.log(` | ||
clusterd: http://localhost:${clusterd.managementPort} | ||
explored: ${daemons.explored.node.apiAddress} | ||
renterds: ${daemons.renterds.map((r) => r.node.apiAddress)} | ||
hostds: ${daemons.hostds.map((h) => h.node.apiAddress)} | ||
walletds: ${daemons.walletds.map((w) => w.node.apiAddress)} | ||
`) | ||
// Set the explorerd address cookie so that the explorer app overrides the | ||
// normal zen address with the testnet cluster address. | ||
await context.addCookies([ | ||
{ | ||
// This should match `exploredCustomApiCookieName` in apps/explorer/config/explored.ts | ||
name: 'exploredAddress', | ||
value: daemons.explored.node.apiAddress, | ||
url: 'http://localhost:3005', | ||
}, | ||
]) | ||
return { | ||
clusterd, | ||
daemons, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import { Walletd } from '@siafoundation/walletd-js' | ||
import { blake2bHex } from 'blakejs' | ||
import { WalletAddressMetadata } from '@siafoundation/walletd-types' | ||
import { to } from '@siafoundation/request' | ||
import { mine } from '@siafoundation/clusterd' | ||
import { humanSiacoin } from '@siafoundation/units' | ||
import { Cluster } from './cluster' | ||
|
||
export async function addWalletToWalletd(walletd: ReturnType<typeof Walletd>) { | ||
// For some reason when this code runs on GitHub Actions it throws an ESM | ||
// import error for the SDK. Running locally it works fine. | ||
// Error: require() of ES Module sdk/index.esm.js from /fixtures/walletd.ts not supported. | ||
// This dynamic import is a workaround. | ||
const { initSDK, getSDK } = await import('@siafoundation/sdk') | ||
await initSDK() | ||
const sdk = getSDK() | ||
const { phrase: mnemonic } = sdk.wallet.generateSeedPhrase() | ||
const mnemonicHash = blake2bHex(mnemonic) | ||
const [wallet, walletError] = await to( | ||
walletd.walletAdd({ | ||
data: { | ||
name: 'test', | ||
description: 'test', | ||
metadata: { | ||
type: 'seed', | ||
mnemonicHash, | ||
}, | ||
}, | ||
}) | ||
) | ||
if (!wallet || walletError) { | ||
throw new Error(`Failed to add wallet: ${walletError}`) | ||
} | ||
const kp = sdk.wallet.keyPairFromSeedPhrase(mnemonic, 0) | ||
const suh = sdk.wallet.standardUnlockHash(kp.publicKey) | ||
const uc = sdk.wallet.standardUnlockConditions(kp.publicKey) | ||
const metadata: WalletAddressMetadata = { | ||
index: 0, | ||
} | ||
const [, addressError] = await to( | ||
walletd.walletAddressAdd({ | ||
params: { | ||
id: wallet.id, | ||
}, | ||
data: { | ||
address: suh.address, | ||
description: '', | ||
spendPolicy: { | ||
type: 'uc', | ||
policy: uc.unlockConditions, | ||
}, | ||
metadata, | ||
}, | ||
}) | ||
) | ||
if (addressError) { | ||
throw new Error(`Failed to add address: ${addressError}`) | ||
} | ||
return { wallet, address: suh.address } | ||
} | ||
|
||
// renterd cluster nodes have siacoins we can use to fund other wallets. | ||
export async function sendSiacoinFromRenterd( | ||
renterd: Cluster['daemons']['renterds'][number], | ||
address: string, | ||
amount: string | ||
) { | ||
console.log(`Sending ${humanSiacoin(amount)} from renterd to:`, address) | ||
try { | ||
// Send some funds to the wallet. | ||
await renterd.api.walletSend({ | ||
data: { | ||
address, | ||
amount, | ||
subtractMinerFee: false, | ||
}, | ||
}) | ||
await mine(1) | ||
} catch (e) { | ||
console.log('error sending siacoin', e) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,53 +1,102 @@ | ||
import { test, expect } from '@playwright/test' | ||
import { ExplorerApp } from '../fixtures/ExplorerApp' | ||
import { TEST_ADDRESS_1 } from '../fixtures/constants' | ||
import { keys } from '../utils' | ||
import { startCluster } from '../fixtures/cluster' | ||
import { | ||
mine, | ||
renterdWaitForContracts, | ||
teardownCluster, | ||
} from '@siafoundation/clusterd' | ||
import { addWalletToWalletd, sendSiacoinFromRenterd } from '../fixtures/walletd' | ||
import { Cluster } from '../fixtures/cluster' | ||
import { toHastings } from '@siafoundation/units' | ||
|
||
let explorerApp: ExplorerApp | ||
let cluster: Cluster | ||
|
||
test.beforeEach(async ({ page }) => { | ||
test.beforeEach(async ({ page, context }) => { | ||
// Start the cluster which includes an explored node, a renterd node, a | ||
// walletd node, and 3 hostd nodes. | ||
cluster = await startCluster({ context }) | ||
// The renterd node will automatically form contracts with the hostd nodes. | ||
// Wait for the contracts to form so that all related activity is on the | ||
// blockchain and visible to explored. | ||
await renterdWaitForContracts({ | ||
renterdNode: cluster.daemons.renterds[0].node, | ||
hostdCount: cluster.daemons.hostds.length, | ||
}) | ||
explorerApp = new ExplorerApp(page) | ||
}) | ||
|
||
test.afterEach(async () => { | ||
await teardownCluster() | ||
}) | ||
|
||
test('address can be searched by id', async ({ page }) => { | ||
const wallet = await cluster.daemons.renterds[0].api.wallet() | ||
await explorerApp.goTo('/') | ||
await explorerApp.navigateBySearchBar(TEST_ADDRESS_1.id) | ||
|
||
await expect(page.getByText(TEST_ADDRESS_1.display.title)).toBeVisible() | ||
await explorerApp.navigateBySearchBar(wallet.data.address) | ||
await expect( | ||
page | ||
.getByTestId('entity-heading') | ||
.getByText(`Address ${wallet.data.address.slice(0, 5)}`) | ||
).toBeVisible() | ||
}) | ||
|
||
test('address can be directly navigated to by id', async ({ page }) => { | ||
await explorerApp.goTo('/address/' + TEST_ADDRESS_1.id) | ||
|
||
await expect(page.getByText(TEST_ADDRESS_1.display.title)).toBeVisible() | ||
const wallet = await cluster.daemons.renterds[0].api.wallet() | ||
await explorerApp.goTo('/address/' + wallet.data.address) | ||
await expect( | ||
page | ||
.getByTestId('entity-heading') | ||
.getByText(`Address ${wallet.data.address.slice(0, 5)}`) | ||
).toBeVisible() | ||
}) | ||
|
||
test('address displays the intended data', async ({ page }) => { | ||
const displayKeys = keys(TEST_ADDRESS_1.display) | ||
|
||
await explorerApp.goTo('/address/' + TEST_ADDRESS_1.id) | ||
|
||
for (const key of displayKeys) { | ||
const currentProperty = TEST_ADDRESS_1.display[key] | ||
await expect(page.getByText(currentProperty)).toBeVisible() | ||
} | ||
}) | ||
|
||
test('address can navigate to the unspent outputs list', async ({ page }) => { | ||
await explorerApp.goTo('/address/' + TEST_ADDRESS_1.id) | ||
const walletd = cluster.daemons.walletds[0] | ||
const { wallet, address } = await addWalletToWalletd(walletd.api) | ||
await sendSiacoinFromRenterd( | ||
cluster.daemons.renterds[0], | ||
address, | ||
toHastings(1_000_000).toString() | ||
) | ||
await mine(10) | ||
const events = await walletd.api.walletEvents({ | ||
params: { | ||
id: wallet.id, | ||
limit: 1_000, | ||
offset: 0, | ||
}, | ||
}) | ||
const outputs = await walletd.api.walletOutputsSiacoin({ | ||
params: { | ||
id: wallet.id, | ||
}, | ||
}) | ||
await explorerApp.goTo('/address/' + address) | ||
await expect(page.getByText(`Address ${address.slice(0, 5)}`)).toBeVisible() | ||
await expect(page.getByText(events.data[0].id.slice(0, 5))).toBeVisible() | ||
await expect(page.getByText(`${events.data.length} events`)).toBeVisible() | ||
await page.getByRole('tab').getByText('Unspent outputs').click() | ||
|
||
await expect(page.getByText('Siacoin output').first()).toBeVisible() | ||
await expect( | ||
page.getByText(outputs.data.outputs[0].id.slice(0, 5)) | ||
).toBeVisible() | ||
}) | ||
|
||
test('address can navigate through to a transaction', async ({ page }) => { | ||
await explorerApp.goTo('/address/' + TEST_ADDRESS_1.id) | ||
const wallet = await cluster.daemons.renterds[0].api.wallet() | ||
const events = await cluster.daemons.renterds[0].api.walletEvents({ | ||
params: { | ||
limit: 1, | ||
offset: 0, | ||
}, | ||
}) | ||
await explorerApp.goTo('/address/' + wallet.data.address) | ||
|
||
await page | ||
.locator( | ||
'a[data-testid="entity-link"][href*="b24a7d623b82206cd3363db0c0d41446f106a59227d86ef081d601315dbd8cca"]' | ||
) | ||
.locator(`a[data-testid="entity-link"][href*="${events.data[0].id}"]`) | ||
.click() | ||
|
||
await expect(page.getByText('Transaction b24a7d623b82206...')).toBeVisible() | ||
await expect(page.getByText(events.data[0].id.slice(0, 5))).toBeVisible() | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.