diff --git a/CHANGELOG.md b/CHANGELOG.md index 2decf987..80544e5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,22 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Changed ### Fixed +## [0.6.2] + +### Added +- feat(common): add app and role id options to hApp installation +- feat(common): add mem-proofs to installAgentsHapps (#139) +- feat(trycp): add multiple clients by array of URLs +- feat(trycp): add multiple clients/players + +### Fixed +- fix(trycp): allow for multiple hosts in scenario (#136) + +## [0.6.1] + +### Changed +- Fixture: split zome into integrity and coordinator zomes [PR \#143](https://github.com/holochain/tryorama/pull/143) + ## [0.6.0] ### Changed diff --git a/Cargo.lock b/Cargo.lock index 762f9acc..a0d9cdbf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -206,6 +206,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "coordinator" +version = "0.1.0" +dependencies = [ + "hdk", + "integrity", + "serde", +] + [[package]] name = "core-foundation" version = "0.9.3" @@ -345,14 +354,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "crud" -version = "0.1.0" -dependencies = [ - "hdk", - "serde", -] - [[package]] name = "darling" version = "0.13.4" @@ -1006,6 +1007,14 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "integrity" +version = "0.1.0" +dependencies = [ + "holochain_deterministic_integrity", + "serde", +] + [[package]] name = "intervallum" version = "1.4.0" diff --git a/Cargo.toml b/Cargo.toml index f135d080..978b995d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,8 @@ [workspace] members = [ "crates/trycp_server", - "ts/test/fixture/zomes/entry" + "ts/test/fixture/zomes/coordinator", + "ts/test/fixture/zomes/integrity" ] [profile.dev] diff --git a/README.md b/README.md index bd3f3953..61935301 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ test("Create 2 players and create and read an entry", async (t) => { // The cells of the installed hApp are returned in the same order as the DNAs // that were passed into the player creation. const createEntryHash: ActionHash = await alice.cells[0].callZome({ - zome_name: "crud", + zome_name: "coordinator", fn_name: "create", payload: content, }); @@ -66,7 +66,7 @@ test("Create 2 players and create and read an entry", async (t) => { // Using the same cell and zome as before, the second player reads the // created entry. const readContent: typeof content = await bob.cells[0].callZome({ - zome_name: "crud", + zome_name: "coordinator", fn_name: "read", payload: createEntryHash, }); @@ -100,7 +100,7 @@ test("Create 2 players and create and read an entry", async (t) => { const content = "Hello Tryorama"; const createEntryHash: EntryHash = await alice.cells[0].callZome({ - zome_name: "crud", + zome_name: "coordinator", fn_name: "create", payload: content, }); @@ -108,7 +108,7 @@ test("Create 2 players and create and read an entry", async (t) => { await pause(100); const readContent: typeof content = await bob.cells[0].callZome({ - zome_name: "crud", + zome_name: "coordinator", fn_name: "read", payload: createEntryHash, }); @@ -184,7 +184,7 @@ Here is the above example that just uses a `Conductor` without a `Scenario`: const entryContent = "test-content"; const createEntryHash: EntryHash = await aliceHapps.cells[0].callZome({ - zome_name: "crud", + zome_name: "coordinator", fn_name: "create", payload: entryContent, }); @@ -193,7 +193,7 @@ Here is the above example that just uses a `Conductor` without a `Scenario`: const readEntryResponse: typeof entryContent = await bobHapps.cells[0].callZome({ - zome_name: "crud", + zome_name: "coordinator", fn_name: "read", payload: createEntryHash, }); @@ -226,7 +226,7 @@ const [aliceHapps] = await conductor.installAgentsHapps({ const entryContent = "test-content"; const createEntryHash: EntryHash = await aliceHapps.cells[0].callZome({ - zome_name: "crud", + zome_name: "coordinator", fn_name: "create", payload: entryContent, }); @@ -248,12 +248,12 @@ const [aliceHapps] = await conductor.installAgentsHapps({ agentsDnas: [dnas], }); const createEntryHash: EntryHash = await aliceHapps.cells[0].callZome({ - zome_name: "crud", + zome_name: "coordinator", fn_name: "create", payload: entryContent, }); const readEntryHash: string = await aliceHapps.cells[0].callZome({ - zome_name: "crud", + zome_name: "coordinator", fn_name: "read", payload: createEntryHash, }); @@ -265,12 +265,12 @@ the shorthand access to the Zome can be called const [aliceHapps] = await conductor.installAgentsHapps({ agentsDnas: [dnas], }); -const aliceCrudZomeCall = getZomeCaller(aliceHapps.cells[0], "crud"); -const entryHeaderHash: ActionHash = await crudZomeCall( +const aliceCallCoordinatorZome = getZomeCaller(aliceHapps.cells[0], "coordinator"); +const entryHeaderHash: ActionHash = await aliceCallCoordinatorZome( "create", "test-entry" ); -const readEntryHash: string = await crudZomeCall( +const readEntryHash: string = await aliceCallCoordinatorZome( "read", entryHeaderHash ); @@ -298,7 +298,7 @@ const alice = await scenario.addPlayerWithHapp(dna, signalHandler); const signal = { value: "hello alice" }; alice.cells[0].callZome({ - zome_name: "crud", + zome_name: "coordinator", fn_name: "signal_loopback", payload: signal, }); diff --git a/docs/tryorama.agentdnas.agentpubkey.md b/docs/tryorama.agentdnas.agentpubkey.md new file mode 100644 index 00000000..5f94f0d9 --- /dev/null +++ b/docs/tryorama.agentdnas.agentpubkey.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [AgentDnas](./tryorama.agentdnas.md) > [agentPubKey](./tryorama.agentdnas.agentpubkey.md) + +## AgentDnas.agentPubKey property + +Signature: + +```typescript +agentPubKey?: AgentPubKey; +``` diff --git a/docs/tryorama.agentdnas.dnas.md b/docs/tryorama.agentdnas.dnas.md new file mode 100644 index 00000000..bb640d58 --- /dev/null +++ b/docs/tryorama.agentdnas.dnas.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [AgentDnas](./tryorama.agentdnas.md) > [dnas](./tryorama.agentdnas.dnas.md) + +## AgentDnas.dnas property + +Signature: + +```typescript +dnas: Dna[]; +``` diff --git a/docs/tryorama.agentdnas.md b/docs/tryorama.agentdnas.md new file mode 100644 index 00000000..8be965d2 --- /dev/null +++ b/docs/tryorama.agentdnas.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [AgentDnas](./tryorama.agentdnas.md) + +## AgentDnas interface + +DNAs per agent. Optionally an agent pub key. + +Signature: + +```typescript +export interface AgentDnas +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [agentPubKey?](./tryorama.agentdnas.agentpubkey.md) | AgentPubKey | (Optional) | +| [dnas](./tryorama.agentdnas.dnas.md) | [Dna](./tryorama.dna.md)\[\] | | + diff --git a/docs/tryorama.agenthappoptions.md b/docs/tryorama.agenthappoptions.md deleted file mode 100644 index eaad06c4..00000000 --- a/docs/tryorama.agenthappoptions.md +++ /dev/null @@ -1,17 +0,0 @@ - - -[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [AgentHappOptions](./tryorama.agenthappoptions.md) - -## AgentHappOptions type - -A type that specifies either only the DNAs that the hApp to be installed consists of, or the DNAs and a signal handler to be registered. - -Signature: - -```typescript -export declare type AgentHappOptions = DnaSource[] | { - dnas: DnaSource[]; - signalHandler?: AppSignalCb; - properties?: DnaProperties; -}; -``` diff --git a/docs/tryorama.agentshappsoptions.md b/docs/tryorama.agentshappsoptions.md new file mode 100644 index 00000000..93e17763 --- /dev/null +++ b/docs/tryorama.agentshappsoptions.md @@ -0,0 +1,19 @@ + + +[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [AgentsHappsOptions](./tryorama.agentshappsoptions.md) + +## AgentsHappsOptions type + +An array of DNA sources for each agent (2-dimensional array) or an array of DNAs and an optional agent pub key. Optionally a UID to be used for DNA installation. + +Signature: + +```typescript +export declare type AgentsHappsOptions = DnaSource[][] | { + agentsDnas: AgentDnas[]; + uid?: string; + installedAppId?: InstalledAppId; +}; +``` +References: [AgentDnas](./tryorama.agentdnas.md) + diff --git a/docs/tryorama.cleanalltrycpconductors.md b/docs/tryorama.cleanalltrycpconductors.md deleted file mode 100644 index 27fc4baf..00000000 --- a/docs/tryorama.cleanalltrycpconductors.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [cleanAllTryCpConductors](./tryorama.cleanalltrycpconductors.md) - -## cleanAllTryCpConductors variable - -Run the `reset` command on the TryCP server to delete all conductor data. - -Signature: - -```typescript -cleanAllTryCpConductors: (serverUrl: URL) => Promise -``` diff --git a/docs/tryorama.clientsplayersoptions.agentpubkeys.md b/docs/tryorama.clientsplayersoptions.agentpubkeys.md new file mode 100644 index 00000000..a64ed540 --- /dev/null +++ b/docs/tryorama.clientsplayersoptions.agentpubkeys.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [ClientsPlayersOptions](./tryorama.clientsplayersoptions.md) > [agentPubKeys](./tryorama.clientsplayersoptions.agentpubkeys.md) + +## ClientsPlayersOptions.agentPubKeys property + +A list of previously generated agent pub keys (optional). + +Signature: + +```typescript +agentPubKeys?: AgentPubKey[]; +``` diff --git a/docs/tryorama.clientsplayersoptions.clienttimeout.md b/docs/tryorama.clientsplayersoptions.clienttimeout.md new file mode 100644 index 00000000..db9db98d --- /dev/null +++ b/docs/tryorama.clientsplayersoptions.clienttimeout.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [ClientsPlayersOptions](./tryorama.clientsplayersoptions.md) > [clientTimeout](./tryorama.clientsplayersoptions.clienttimeout.md) + +## ClientsPlayersOptions.clientTimeout property + +A timeout for the web socket connection (optional). + +Signature: + +```typescript +clientTimeout?: number; +``` diff --git a/docs/tryorama.clientsplayersoptions.dnas.md b/docs/tryorama.clientsplayersoptions.dnas.md new file mode 100644 index 00000000..239cc6ca --- /dev/null +++ b/docs/tryorama.clientsplayersoptions.dnas.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [ClientsPlayersOptions](./tryorama.clientsplayersoptions.md) > [dnas](./tryorama.clientsplayersoptions.dnas.md) + +## ClientsPlayersOptions.dnas property + +An array of DNAs that will be installed for each agent (optional). + +Signature: + +```typescript +dnas?: Dna[]; +``` diff --git a/docs/tryorama.clientsplayersoptions.md b/docs/tryorama.clientsplayersoptions.md new file mode 100644 index 00000000..809a29c2 --- /dev/null +++ b/docs/tryorama.clientsplayersoptions.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [ClientsPlayersOptions](./tryorama.clientsplayersoptions.md) + +## ClientsPlayersOptions interface + + +Signature: + +```typescript +export interface ClientsPlayersOptions +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [agentPubKeys?](./tryorama.clientsplayersoptions.agentpubkeys.md) | AgentPubKey\[\] | (Optional) A list of previously generated agent pub keys (optional). | +| [clientTimeout?](./tryorama.clientsplayersoptions.clienttimeout.md) | number | (Optional) A timeout for the web socket connection (optional). | +| [dnas?](./tryorama.clientsplayersoptions.dnas.md) | [Dna](./tryorama.dna.md)\[\] | (Optional) An array of DNAs that will be installed for each agent (optional). | +| [numberOfAgentsPerConductor?](./tryorama.clientsplayersoptions.numberofagentsperconductor.md) | number | (Optional) Number of agents per conductor. Defaults to 1. Requires dnas to be specified. | +| [numberOfConductorsPerClient?](./tryorama.clientsplayersoptions.numberofconductorsperclient.md) | number | (Optional) Number of conductors per client. Default to 1. | +| [signalHandler?](./tryorama.clientsplayersoptions.signalhandler.md) | AppSignalCb | (Optional) A signal handler to be registered in conductors. | + diff --git a/docs/tryorama.clientsplayersoptions.numberofagentsperconductor.md b/docs/tryorama.clientsplayersoptions.numberofagentsperconductor.md new file mode 100644 index 00000000..a2fe87bd --- /dev/null +++ b/docs/tryorama.clientsplayersoptions.numberofagentsperconductor.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [ClientsPlayersOptions](./tryorama.clientsplayersoptions.md) > [numberOfAgentsPerConductor](./tryorama.clientsplayersoptions.numberofagentsperconductor.md) + +## ClientsPlayersOptions.numberOfAgentsPerConductor property + +Number of agents per conductor. Defaults to 1. Requires `dnas` to be specified. + +Signature: + +```typescript +numberOfAgentsPerConductor?: number; +``` diff --git a/docs/tryorama.clientsplayersoptions.numberofconductorsperclient.md b/docs/tryorama.clientsplayersoptions.numberofconductorsperclient.md new file mode 100644 index 00000000..ba19a03d --- /dev/null +++ b/docs/tryorama.clientsplayersoptions.numberofconductorsperclient.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [ClientsPlayersOptions](./tryorama.clientsplayersoptions.md) > [numberOfConductorsPerClient](./tryorama.clientsplayersoptions.numberofconductorsperclient.md) + +## ClientsPlayersOptions.numberOfConductorsPerClient property + +Number of conductors per client. Default to 1. + +Signature: + +```typescript +numberOfConductorsPerClient?: number; +``` diff --git a/docs/tryorama.clientsplayersoptions.signalhandler.md b/docs/tryorama.clientsplayersoptions.signalhandler.md new file mode 100644 index 00000000..94e89dcc --- /dev/null +++ b/docs/tryorama.clientsplayersoptions.signalhandler.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [ClientsPlayersOptions](./tryorama.clientsplayersoptions.md) > [signalHandler](./tryorama.clientsplayersoptions.signalhandler.md) + +## ClientsPlayersOptions.signalHandler property + +A signal handler to be registered in conductors. + +Signature: + +```typescript +signalHandler?: AppSignalCb; +``` diff --git a/docs/tryorama.conductor.installagentshapps.md b/docs/tryorama.conductor.installagentshapps.md index 2cd714df..80340194 100644 --- a/docs/tryorama.conductor.installagentshapps.md +++ b/docs/tryorama.conductor.installagentshapps.md @@ -9,18 +9,14 @@ Install a set of DNAs for multiple agents into the conductor. Signature: ```typescript -installAgentsHapps(options: { - agentsDnas: DnaSource[][]; - uid?: string; - properties?: DnaProperties; - }): Promise; +installAgentsHapps(options: AgentsHappsOptions): Promise; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| options | { agentsDnas: DnaSource\[\]\[\]; uid?: string; properties?: DnaProperties; } | An array of DNAs for each agent, resulting in a 2-dimensional array, and a UID for the DNAs (optional). | +| options | [AgentsHappsOptions](./tryorama.agentshappsoptions.md) | An array of DNAs for each agent, resulting in a 2-dimensional array, and a UID for the DNAs (optional). | Returns: diff --git a/docs/tryorama.createtrycpconductor.md b/docs/tryorama.createtrycpconductor.md index cfe053a2..ca97bdb2 100644 --- a/docs/tryorama.createtrycpconductor.md +++ b/docs/tryorama.createtrycpconductor.md @@ -4,10 +4,10 @@ ## createTryCpConductor variable -The function to create a TryCP Conductor (aka "Player"). +The function to create a TryCP Conductor. By default configures and starts it. Signature: ```typescript -createTryCpConductor: (serverUrl: URL, options?: TryCpConductorOptions) => Promise +createTryCpConductor: (tryCpClient: TryCpClient, options?: TryCpConductorOptions) => Promise ``` diff --git a/docs/tryorama.dna.md b/docs/tryorama.dna.md new file mode 100644 index 00000000..68da2eb1 --- /dev/null +++ b/docs/tryorama.dna.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [Dna](./tryorama.dna.md) + +## Dna interface + +DNA source and additional options. + +Signature: + +```typescript +export interface Dna +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [membraneProof?](./tryorama.dna.membraneproof.md) | MembraneProof | (Optional) | +| [properties?](./tryorama.dna.properties.md) | DnaProperties | (Optional) | +| [roleId?](./tryorama.dna.roleid.md) | string | (Optional) | +| [source](./tryorama.dna.source.md) | DnaSource | | + diff --git a/docs/tryorama.dna.membraneproof.md b/docs/tryorama.dna.membraneproof.md new file mode 100644 index 00000000..71c4464a --- /dev/null +++ b/docs/tryorama.dna.membraneproof.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [Dna](./tryorama.dna.md) > [membraneProof](./tryorama.dna.membraneproof.md) + +## Dna.membraneProof property + +Signature: + +```typescript +membraneProof?: MembraneProof; +``` diff --git a/docs/tryorama.dna.properties.md b/docs/tryorama.dna.properties.md new file mode 100644 index 00000000..2e43ecca --- /dev/null +++ b/docs/tryorama.dna.properties.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [Dna](./tryorama.dna.md) > [properties](./tryorama.dna.properties.md) + +## Dna.properties property + +Signature: + +```typescript +properties?: DnaProperties; +``` diff --git a/docs/tryorama.dna.roleid.md b/docs/tryorama.dna.roleid.md new file mode 100644 index 00000000..81dd48c6 --- /dev/null +++ b/docs/tryorama.dna.roleid.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [Dna](./tryorama.dna.md) > [roleId](./tryorama.dna.roleid.md) + +## Dna.roleId property + +Signature: + +```typescript +roleId?: string; +``` diff --git a/docs/tryorama.dna.source.md b/docs/tryorama.dna.source.md new file mode 100644 index 00000000..8254df8f --- /dev/null +++ b/docs/tryorama.dna.source.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [Dna](./tryorama.dna.md) > [source](./tryorama.dna.source.md) + +## Dna.source property + +Signature: + +```typescript +source: DnaSource; +``` diff --git a/docs/tryorama.iconductor.installagentshapps.md b/docs/tryorama.iconductor.installagentshapps.md index e021b616..3c2616cf 100644 --- a/docs/tryorama.iconductor.installagentshapps.md +++ b/docs/tryorama.iconductor.installagentshapps.md @@ -7,10 +7,5 @@ Signature: ```typescript -installAgentsHapps: (options: { - agentsDnas: DnaSource[][]; - uid?: string; - properties?: DnaProperties; - signalHandler?: AppSignalCb; - }) => Promise; +installAgentsHapps: (options: AgentsHappsOptions) => Promise; ``` diff --git a/docs/tryorama.iconductor.md b/docs/tryorama.iconductor.md index 53ebf075..277f717e 100644 --- a/docs/tryorama.iconductor.md +++ b/docs/tryorama.iconductor.md @@ -18,7 +18,7 @@ export interface IConductor | --- | --- | --- | | [adminWs](./tryorama.iconductor.adminws.md) | () => Omit<AdminWebsocket, "\_requester" \| "client" \| "activateApp" \| "deactivateApp" \| "defaultTimeout" \| "listActiveApps"> | | | [appWs](./tryorama.iconductor.appws.md) | () => Pick<AppWebsocket, "callZome" \| "appInfo"> | | -| [installAgentsHapps](./tryorama.iconductor.installagentshapps.md) | (options: { agentsDnas: DnaSource\[\]\[\]; uid?: string; properties?: DnaProperties; signalHandler?: AppSignalCb; }) => Promise<[AgentHapp](./tryorama.agenthapp.md)\[\]> | | +| [installAgentsHapps](./tryorama.iconductor.installagentshapps.md) | (options: [AgentsHappsOptions](./tryorama.agentshappsoptions.md)) => Promise<[AgentHapp](./tryorama.agenthapp.md)\[\]> | | | [shutDown](./tryorama.iconductor.shutdown.md) | () => Promise<number \| null> | | | [startUp](./tryorama.iconductor.startup.md) | () => Promise<void \| null> | | diff --git a/docs/tryorama.iscenario.addconductor.md b/docs/tryorama.iscenario.addconductor.md deleted file mode 100644 index b722a491..00000000 --- a/docs/tryorama.iscenario.addconductor.md +++ /dev/null @@ -1,22 +0,0 @@ - - -[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [IScenario](./tryorama.iscenario.md) > [addConductor](./tryorama.iscenario.addconductor.md) - -## IScenario.addConductor() method - -Signature: - -```typescript -addConductor(signalHandler?: AppSignalCb): Promise; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| signalHandler | AppSignalCb | (Optional) | - -Returns: - -Promise<[IConductor](./tryorama.iconductor.md)> - diff --git a/docs/tryorama.iscenario.addplayerswithhappbundles.md b/docs/tryorama.iscenario.addplayerswithhappbundles.md deleted file mode 100644 index 6645aef8..00000000 --- a/docs/tryorama.iscenario.addplayerswithhappbundles.md +++ /dev/null @@ -1,27 +0,0 @@ - - -[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [IScenario](./tryorama.iscenario.md) > [addPlayersWithHappBundles](./tryorama.iscenario.addplayerswithhappbundles.md) - -## IScenario.addPlayersWithHappBundles() method - -Signature: - -```typescript -addPlayersWithHappBundles(playersHappBundles: Array<{ - appBundleSource: AppBundleSource; - options?: HappBundleOptions & { - signalHandler?: AppSignalCb; - }; - }>): Promise; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| playersHappBundles | Array<{ appBundleSource: AppBundleSource; options?: [HappBundleOptions](./tryorama.happbundleoptions.md) & { signalHandler?: AppSignalCb; }; }> | | - -Returns: - -Promise<[IPlayer](./tryorama.iplayer.md)\[\]> - diff --git a/docs/tryorama.iscenario.addplayerswithhapps.md b/docs/tryorama.iscenario.addplayerswithhapps.md deleted file mode 100644 index 8ebdb780..00000000 --- a/docs/tryorama.iscenario.addplayerswithhapps.md +++ /dev/null @@ -1,22 +0,0 @@ - - -[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [IScenario](./tryorama.iscenario.md) > [addPlayersWithHapps](./tryorama.iscenario.addplayerswithhapps.md) - -## IScenario.addPlayersWithHapps() method - -Signature: - -```typescript -addPlayersWithHapps(agentHappOptions: AgentHappOptions[]): Promise; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| agentHappOptions | [AgentHappOptions](./tryorama.agenthappoptions.md)\[\] | | - -Returns: - -Promise<[IPlayer](./tryorama.iplayer.md)\[\]> - diff --git a/docs/tryorama.iscenario.addplayerwithhapp.md b/docs/tryorama.iscenario.addplayerwithhapp.md deleted file mode 100644 index 3024a8ee..00000000 --- a/docs/tryorama.iscenario.addplayerwithhapp.md +++ /dev/null @@ -1,22 +0,0 @@ - - -[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [IScenario](./tryorama.iscenario.md) > [addPlayerWithHapp](./tryorama.iscenario.addplayerwithhapp.md) - -## IScenario.addPlayerWithHapp() method - -Signature: - -```typescript -addPlayerWithHapp(agentHappOptions: AgentHappOptions): Promise; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| agentHappOptions | [AgentHappOptions](./tryorama.agenthappoptions.md) | | - -Returns: - -Promise<[IPlayer](./tryorama.iplayer.md)> - diff --git a/docs/tryorama.iscenario.addplayerwithhappbundle.md b/docs/tryorama.iscenario.addplayerwithhappbundle.md deleted file mode 100644 index 902745a7..00000000 --- a/docs/tryorama.iscenario.addplayerwithhappbundle.md +++ /dev/null @@ -1,25 +0,0 @@ - - -[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [IScenario](./tryorama.iscenario.md) > [addPlayerWithHappBundle](./tryorama.iscenario.addplayerwithhappbundle.md) - -## IScenario.addPlayerWithHappBundle() method - -Signature: - -```typescript -addPlayerWithHappBundle(appBundleSource: AppBundleSource, options?: HappBundleOptions & { - signalHandler?: AppSignalCb; - }): Promise; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| appBundleSource | AppBundleSource | | -| options | [HappBundleOptions](./tryorama.happbundleoptions.md) & { signalHandler?: AppSignalCb; } | (Optional) | - -Returns: - -Promise<[IPlayer](./tryorama.iplayer.md)> - diff --git a/docs/tryorama.iscenario.md b/docs/tryorama.iscenario.md deleted file mode 100644 index 238eb49d..00000000 --- a/docs/tryorama.iscenario.md +++ /dev/null @@ -1,27 +0,0 @@ - - -[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [IScenario](./tryorama.iscenario.md) - -## IScenario interface - -Base interface of a Tryorama test scenario. Both [Scenario](./tryorama.scenario.md) and [TryCpScenario](./tryorama.trycpscenario.md) implement this interface. - -Signature: - -```typescript -export interface IScenario -``` - -## Methods - -| Method | Description | -| --- | --- | -| [addConductor(signalHandler)](./tryorama.iscenario.addconductor.md) | | -| [addPlayersWithHappBundles(playersHappBundles)](./tryorama.iscenario.addplayerswithhappbundles.md) | | -| [addPlayersWithHapps(agentHappOptions)](./tryorama.iscenario.addplayerswithhapps.md) | | -| [addPlayerWithHapp(agentHappOptions)](./tryorama.iscenario.addplayerwithhapp.md) | | -| [addPlayerWithHappBundle(appBundleSource, options)](./tryorama.iscenario.addplayerwithhappbundle.md) | | -| [cleanUp()](./tryorama.iscenario.cleanup.md) | | -| [shareAllAgents(conductors)](./tryorama.iscenario.shareallagents.md) | | -| [shutDown()](./tryorama.iscenario.shutdown.md) | | - diff --git a/docs/tryorama.iscenario.shareallagents.md b/docs/tryorama.iscenario.shareallagents.md deleted file mode 100644 index c3d7bc16..00000000 --- a/docs/tryorama.iscenario.shareallagents.md +++ /dev/null @@ -1,22 +0,0 @@ - - -[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [IScenario](./tryorama.iscenario.md) > [shareAllAgents](./tryorama.iscenario.shareallagents.md) - -## IScenario.shareAllAgents() method - -Signature: - -```typescript -shareAllAgents(conductors: IConductor[]): Promise; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| conductors | [IConductor](./tryorama.iconductor.md)\[\] | | - -Returns: - -Promise<void> - diff --git a/docs/tryorama.iscenario.shutdown.md b/docs/tryorama.iscenario.shutdown.md deleted file mode 100644 index ed1c0d6c..00000000 --- a/docs/tryorama.iscenario.shutdown.md +++ /dev/null @@ -1,15 +0,0 @@ - - -[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [IScenario](./tryorama.iscenario.md) > [shutDown](./tryorama.iscenario.shutdown.md) - -## IScenario.shutDown() method - -Signature: - -```typescript -shutDown(): Promise; -``` -Returns: - -Promise<void> - diff --git a/docs/tryorama.md b/docs/tryorama.md index 88cac647..fcd33ef9 100644 --- a/docs/tryorama.md +++ b/docs/tryorama.md @@ -18,9 +18,9 @@ TryCP stands for Tryorama Control Protocol (TryCP) and is a protocol to enable r | --- | --- | | [Conductor](./tryorama.conductor.md) | A class to manage a conductor running on localhost. | | [Scenario](./tryorama.scenario.md) | An abstraction of a test scenario to write tests against Holochain hApps, running on a local conductor. | -| [TryCpClient](./tryorama.trycpclient.md) | A factory class to create client connections to a running TryCP server. | +| [TryCpClient](./tryorama.trycpclient.md) | A factory class to create client connections to a running TryCP server.With a client, conductors on the server can ba configured, started and stopped. All valid Admin and App API commands can be sent to the server too. | | [TryCpConductor](./tryorama.trycpconductor.md) | A class to manage a conductor running on a TryCP server. | -| [TryCpScenario](./tryorama.trycpscenario.md) | An abstraction of a test scenario to write tests against Holochain hApps, running on a TryCp conductor. | +| [TryCpScenario](./tryorama.trycpscenario.md) | A test scenario abstraction with convenience functions to manage TryCP clients and players (agent + conductor).Clients in turn help manage conductors on TryCP servers. Clients can be added to a scenario to keep track of all server connections. When finishing a test scenario, all conductors of all clients can be easily cleaned up and the client connections closed. | | [TryCpServer](./tryorama.trycpserver.md) | A factory class to start and stop local instances of the TryCP server. | ## Enumerations @@ -51,16 +51,18 @@ TryCP stands for Tryorama Control Protocol (TryCP) and is a protocol to enable r | [AdminApiResponseDnasListed](./tryorama.adminapiresponsednaslisted.md) | | | [AdminApiResponseFullStateDumped](./tryorama.adminapiresponsefullstatedumped.md) | | | [AdminApiResponseStateDumped](./tryorama.adminapiresponsestatedumped.md) | | +| [AgentDnas](./tryorama.agentdnas.md) | DNAs per agent. Optionally an agent pub key. | | [AgentHapp](./tryorama.agenthapp.md) | Provides direct access to cells of a hApp and the agent key. | | [ApiErrorResponse](./tryorama.apierrorresponse.md) | Error response from the Admin or App API. | | [AppApiResponseAppInfo](./tryorama.appapiresponseappinfo.md) | | | [AppApiResponseZomeCall](./tryorama.appapiresponsezomecall.md) | | | [CallableCell](./tryorama.callablecell.md) | Extends an installed cell by a function to call a zome. | +| [ClientsPlayersOptions](./tryorama.clientsplayersoptions.md) | | | [ConductorOptions](./tryorama.conductoroptions.md) | | +| [Dna](./tryorama.dna.md) | DNA source and additional options. | | [HappBundleOptions](./tryorama.happbundleoptions.md) | Optional arguments when installing a hApp bundle. | | [IConductor](./tryorama.iconductor.md) | Base interface of a Tryorama conductor. Both [Conductor](./tryorama.conductor.md) and [TryCpConductor](./tryorama.trycpconductor.md) implement this interface. | | [IPlayer](./tryorama.iplayer.md) | Combines an agent hApp with the conductor they belong to. | -| [IScenario](./tryorama.iscenario.md) | Base interface of a Tryorama test scenario. Both [Scenario](./tryorama.scenario.md) and [TryCpScenario](./tryorama.trycpscenario.md) implement this interface. | | [Player](./tryorama.player.md) | A player tied to a [Conductor](./tryorama.conductor.md). | | [RequestAdminInterfaceData](./tryorama.requestadmininterfacedata.md) | All possible calls to an admin interface. | | [RequestAppInfo](./tryorama.requestappinfo.md) | Request app info from a conductor. | @@ -86,9 +88,8 @@ TryCP stands for Tryorama Control Protocol (TryCP) and is a protocol to enable r | --- | --- | | [addAllAgentsToAllConductors](./tryorama.addallagentstoallconductors.md) | Add all agents of all conductors to each other. Shortcuts peer discovery through a bootstrap server or gossiping. | | [cleanAllConductors](./tryorama.cleanallconductors.md) | Run the hc command to delete all conductor data. | -| [cleanAllTryCpConductors](./tryorama.cleanalltrycpconductors.md) | Run the reset command on the TryCP server to delete all conductor data. | | [createConductor](./tryorama.createconductor.md) | The function to create a conductor. It starts a sandbox conductor via the Holochain CLI. | -| [createTryCpConductor](./tryorama.createtrycpconductor.md) | The function to create a TryCP Conductor (aka "Player"). | +| [createTryCpConductor](./tryorama.createtrycpconductor.md) | The function to create a TryCP Conductor. By default configures and starts it. | | [DEFAULT\_PARTIAL\_PLAYER\_CONFIG](./tryorama.default_partial_player_config.md) | The default partial config for a TryCP conductor. | | [getZomeCaller](./tryorama.getzomecaller.md) | Get a shorthand function to call a cell's zome. | | [pause](./tryorama.pause.md) | A utility function to wait the given amount of time. | @@ -100,12 +101,13 @@ TryCP stands for Tryorama Control Protocol (TryCP) and is a protocol to enable r | Type Alias | Description | | --- | --- | | [AdminApiResponse](./tryorama.adminapiresponse.md) | All possible responses from the Admin API. | -| [AgentHappOptions](./tryorama.agenthappoptions.md) | A type that specifies either only the DNAs that the hApp to be installed consists of, or the DNAs and a signal handler to be registered. | +| [AgentsHappsOptions](./tryorama.agentshappsoptions.md) | An array of DNA sources for each agent (2-dimensional array) or an array of DNAs and an optional agent pub key. Optionally a UID to be used for DNA installation. | | [AppApiResponse](./tryorama.appapiresponse.md) | Possible responses from the App API. | | [CallZomeFn](./tryorama.callzomefn.md) | The function for calling a zome from a specific cell. | | [CellZomeCallRequest](./tryorama.cellzomecallrequest.md) | The zome request options adapted to a specific cell. | | [ConductorId](./tryorama.conductorid.md) | | | [CreateConductorOptions](./tryorama.createconductoroptions.md) | Options for using the conductor factory. | +| [PlayerHappOptions](./tryorama.playerhappoptions.md) | Player installation options used in scenarios.Specifies either only the DNA sources that the hApp to be installed consists of, or the DNAs and a signal handler to be registered. | | [RequestCallAppInterfaceMessage](./tryorama.requestcallappinterfacemessage.md) | All possible calls to an app interface. | | [TryCpApiResponse](./tryorama.trycpapiresponse.md) | Possible responses from the Admin and App APIs. | | [TryCpConductorLogLevel](./tryorama.trycpconductorloglevel.md) | Log level for a TryCP conductor. | diff --git a/docs/tryorama.playerhappoptions.md b/docs/tryorama.playerhappoptions.md new file mode 100644 index 00000000..5b656f04 --- /dev/null +++ b/docs/tryorama.playerhappoptions.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [PlayerHappOptions](./tryorama.playerhappoptions.md) + +## PlayerHappOptions type + +Player installation options used in scenarios. + +Specifies either only the DNA sources that the hApp to be installed consists of, or the DNAs and a signal handler to be registered. + +Signature: + +```typescript +export declare type PlayerHappOptions = DnaSource[] | { + dnas: Dna[]; + signalHandler?: AppSignalCb; +}; +``` +References: [Dna](./tryorama.dna.md) + diff --git a/docs/tryorama.scenario.addplayerswithhapps.md b/docs/tryorama.scenario.addplayerswithhapps.md index bdd27445..73d7e65d 100644 --- a/docs/tryorama.scenario.addplayerswithhapps.md +++ b/docs/tryorama.scenario.addplayerswithhapps.md @@ -9,14 +9,14 @@ Create and add multiple players to the scenario, with a set of DNAs installed fo Signature: ```typescript -addPlayersWithHapps(agentHappOptions: AgentHappOptions[]): Promise; +addPlayersWithHapps(agentHappOptions: PlayerHappOptions[]): Promise; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| agentHappOptions | [AgentHappOptions](./tryorama.agenthappoptions.md)\[\] | [AgentHappOptions](./tryorama.agenthappoptions.md) for each player. | +| agentHappOptions | [PlayerHappOptions](./tryorama.playerhappoptions.md)\[\] | [PlayerHappOptions](./tryorama.playerhappoptions.md) for each player. | Returns: diff --git a/docs/tryorama.scenario.addplayerwithhapp.md b/docs/tryorama.scenario.addplayerwithhapp.md index 8a19f6cb..60344a5c 100644 --- a/docs/tryorama.scenario.addplayerwithhapp.md +++ b/docs/tryorama.scenario.addplayerwithhapp.md @@ -9,14 +9,14 @@ Create and add a single player to the scenario, with a set of DNAs installed. Signature: ```typescript -addPlayerWithHapp(agentHappOptions: AgentHappOptions): Promise; +addPlayerWithHapp(playerHappOptions: PlayerHappOptions): Promise; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| agentHappOptions | [AgentHappOptions](./tryorama.agenthappoptions.md) | [AgentHappOptions](./tryorama.agenthappoptions.md). | +| playerHappOptions | [PlayerHappOptions](./tryorama.playerhappoptions.md) | [PlayerHappOptions](./tryorama.playerhappoptions.md). | Returns: diff --git a/docs/tryorama.scenario.md b/docs/tryorama.scenario.md index 6c38ca0e..9844fb71 100644 --- a/docs/tryorama.scenario.md +++ b/docs/tryorama.scenario.md @@ -9,9 +9,8 @@ An abstraction of a test scenario to write tests against Holochain hApps, runnin Signature: ```typescript -export declare class Scenario implements IScenario +export declare class Scenario ``` -Implements: [IScenario](./tryorama.iscenario.md) ## Constructors @@ -33,7 +32,7 @@ export declare class Scenario implements IScenario | [addConductor(signalHandler)](./tryorama.scenario.addconductor.md) | | Create and add a conductor to the scenario. | | [addPlayersWithHappBundles(playersHappBundles)](./tryorama.scenario.addplayerswithhappbundles.md) | | Create and add multiple players to the scenario, with a hApp bundle installed for each player. | | [addPlayersWithHapps(agentHappOptions)](./tryorama.scenario.addplayerswithhapps.md) | | Create and add multiple players to the scenario, with a set of DNAs installed for each player. | -| [addPlayerWithHapp(agentHappOptions)](./tryorama.scenario.addplayerwithhapp.md) | | Create and add a single player to the scenario, with a set of DNAs installed. | +| [addPlayerWithHapp(playerHappOptions)](./tryorama.scenario.addplayerwithhapp.md) | | Create and add a single player to the scenario, with a set of DNAs installed. | | [addPlayerWithHappBundle(appBundleSource, options)](./tryorama.scenario.addplayerwithhappbundle.md) | | Create and add a single player to the scenario, with a hApp bundle installed. | | [cleanUp()](./tryorama.scenario.cleanup.md) | | Shut down and delete all conductors in the scenario. | | [shareAllAgents()](./tryorama.scenario.shareallagents.md) | | Register all agents of all passed in conductors to each other. This skips peer discovery through gossip and thus accelerates test runs. | diff --git a/docs/tryorama.trycpscenario.addconductor.md b/docs/tryorama.trycpclient.addconductor.md similarity index 71% rename from docs/tryorama.trycpscenario.addconductor.md rename to docs/tryorama.trycpclient.addconductor.md index 0a0a3d38..64c1bda7 100644 --- a/docs/tryorama.trycpscenario.addconductor.md +++ b/docs/tryorama.trycpclient.addconductor.md @@ -1,10 +1,10 @@ -[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [TryCpScenario](./tryorama.trycpscenario.md) > [addConductor](./tryorama.trycpscenario.addconductor.md) +[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [TryCpClient](./tryorama.trycpclient.md) > [addConductor](./tryorama.trycpclient.addconductor.md) -## TryCpScenario.addConductor() method +## TryCpClient.addConductor() method -Create and add a conductor to the scenario. +Create and add a conductor to the client. Signature: diff --git a/docs/tryorama.trycpclient.cleanallconductors.md b/docs/tryorama.trycpclient.cleanallconductors.md new file mode 100644 index 00000000..689d9576 --- /dev/null +++ b/docs/tryorama.trycpclient.cleanallconductors.md @@ -0,0 +1,19 @@ + + +[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [TryCpClient](./tryorama.trycpclient.md) > [cleanAllConductors](./tryorama.trycpclient.cleanallconductors.md) + +## TryCpClient.cleanAllConductors() method + +Run the `reset` command on the TryCP server to delete all conductor data. + +Signature: + +```typescript +cleanAllConductors(): Promise; +``` +Returns: + +Promise<null> + +An empty success response. + diff --git a/docs/tryorama.iscenario.cleanup.md b/docs/tryorama.trycpclient.cleanup.md similarity index 52% rename from docs/tryorama.iscenario.cleanup.md rename to docs/tryorama.trycpclient.cleanup.md index 781170e9..89d2b8ea 100644 --- a/docs/tryorama.iscenario.cleanup.md +++ b/docs/tryorama.trycpclient.cleanup.md @@ -1,8 +1,10 @@ -[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [IScenario](./tryorama.iscenario.md) > [cleanUp](./tryorama.iscenario.cleanup.md) +[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [TryCpClient](./tryorama.trycpclient.md) > [cleanUp](./tryorama.trycpclient.cleanup.md) -## IScenario.cleanUp() method +## TryCpClient.cleanUp() method + +Shut down all registered conductors and delete them, and close the client connection. Signature: diff --git a/docs/tryorama.trycpscenario.conductors.md b/docs/tryorama.trycpclient.conductors.md similarity index 58% rename from docs/tryorama.trycpscenario.conductors.md rename to docs/tryorama.trycpclient.conductors.md index 4d4b20de..0ed51e12 100644 --- a/docs/tryorama.trycpscenario.conductors.md +++ b/docs/tryorama.trycpclient.conductors.md @@ -1,8 +1,8 @@ -[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [TryCpScenario](./tryorama.trycpscenario.md) > [conductors](./tryorama.trycpscenario.conductors.md) +[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [TryCpClient](./tryorama.trycpclient.md) > [conductors](./tryorama.trycpclient.conductors.md) -## TryCpScenario.conductors property +## TryCpClient.conductors property Signature: diff --git a/docs/tryorama.trycpclient.create.md b/docs/tryorama.trycpclient.create.md index cc6a40a9..f60ecda5 100644 --- a/docs/tryorama.trycpclient.create.md +++ b/docs/tryorama.trycpclient.create.md @@ -23,5 +23,5 @@ static create(serverUrl: URL, timeout?: number): Promise; Promise<[TryCpClient](./tryorama.trycpclient.md)> -A client connection. +The created client connection. diff --git a/docs/tryorama.trycpclient.md b/docs/tryorama.trycpclient.md index cd184e95..6b772c76 100644 --- a/docs/tryorama.trycpclient.md +++ b/docs/tryorama.trycpclient.md @@ -6,19 +6,31 @@ A factory class to create client connections to a running TryCP server. +With a client, conductors on the server can ba configured, started and stopped. All valid Admin and App API commands can be sent to the server too. + Signature: ```typescript export declare class TryCpClient ``` +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [conductors](./tryorama.trycpclient.conductors.md) | | [TryCpConductor](./tryorama.trycpconductor.md)\[\] | | + ## Methods | Method | Modifiers | Description | | --- | --- | --- | +| [addConductor(signalHandler)](./tryorama.trycpclient.addconductor.md) | | Create and add a conductor to the client. | | [call(request)](./tryorama.trycpclient.call.md) | | Send a call to the TryCP server. | +| [cleanAllConductors()](./tryorama.trycpclient.cleanallconductors.md) | | Run the reset command on the TryCP server to delete all conductor data. | +| [cleanUp()](./tryorama.trycpclient.cleanup.md) | | Shut down all registered conductors and delete them, and close the client connection. | | [close()](./tryorama.trycpclient.close.md) | | Closes the client connection. | | [create(serverUrl, timeout)](./tryorama.trycpclient.create.md) | static | Create a client connection to a running TryCP server. | | [ping(data)](./tryorama.trycpclient.ping.md) | | Send a ping with data. | | [setSignalHandler(port, signalHandler)](./tryorama.trycpclient.setsignalhandler.md) | | | +| [shutDownConductors()](./tryorama.trycpclient.shutdownconductors.md) | | Shut down all conductors on the connected TryCP server and disconnect their app interfaces. | diff --git a/docs/tryorama.trycpclient.shutdownconductors.md b/docs/tryorama.trycpclient.shutdownconductors.md new file mode 100644 index 00000000..c870db03 --- /dev/null +++ b/docs/tryorama.trycpclient.shutdownconductors.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [TryCpClient](./tryorama.trycpclient.md) > [shutDownConductors](./tryorama.trycpclient.shutdownconductors.md) + +## TryCpClient.shutDownConductors() method + +Shut down all conductors on the connected TryCP server and disconnect their app interfaces. + +Signature: + +```typescript +shutDownConductors(): Promise; +``` +Returns: + +Promise<void> + diff --git a/docs/tryorama.trycpconductor.id.md b/docs/tryorama.trycpconductor.id.md new file mode 100644 index 00000000..d69f27b5 --- /dev/null +++ b/docs/tryorama.trycpconductor.id.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [TryCpConductor](./tryorama.trycpconductor.md) > [id](./tryorama.trycpconductor.id.md) + +## TryCpConductor.id property + +Signature: + +```typescript +readonly id: string; +``` diff --git a/docs/tryorama.trycpconductor.installagentshapps.md b/docs/tryorama.trycpconductor.installagentshapps.md index 9b599ba4..26c7fc11 100644 --- a/docs/tryorama.trycpconductor.installagentshapps.md +++ b/docs/tryorama.trycpconductor.installagentshapps.md @@ -9,19 +9,14 @@ Install a set of DNAs for multiple agents into the conductor. Signature: ```typescript -installAgentsHapps(options: { - agentsDnas: DnaSource[][]; - signalHandler?: AppSignalCb; - uid?: string; - properties?: DnaProperties; - }): Promise; +installAgentsHapps(options: AgentsHappsOptions): Promise; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| options | { agentsDnas: DnaSource\[\]\[\]; signalHandler?: AppSignalCb; uid?: string; properties?: DnaProperties; } | An array of DNAs for each agent, resulting in a 2-dimensional array, and a UID for the DNAs (optional). | +| options | [AgentsHappsOptions](./tryorama.agentshappsoptions.md) | [AgentsHappsOptions](./tryorama.agentshappsoptions.md) | Returns: diff --git a/docs/tryorama.trycpconductor.installhappbundle.md b/docs/tryorama.trycpconductor.installhappbundle.md index e90699cb..05fe9ff9 100644 --- a/docs/tryorama.trycpconductor.installhappbundle.md +++ b/docs/tryorama.trycpconductor.installhappbundle.md @@ -9,12 +9,7 @@ Install a hApp bundle into the conductor. Signature: ```typescript -installHappBundle(appBundleSource: AppBundleSource, options?: { - agentPubKey?: AgentPubKey; - installedAppId?: string; - uid?: string; - membraneProofs?: Record; - }): Promise; +installHappBundle(appBundleSource: AppBundleSource, options?: HappBundleOptions): Promise; ``` ## Parameters @@ -22,7 +17,7 @@ installHappBundle(appBundleSource: AppBundleSource, options?: { | Parameter | Type | Description | | --- | --- | --- | | appBundleSource | AppBundleSource | The bundle or path to the bundle. | -| options | { agentPubKey?: AgentPubKey; installedAppId?: string; uid?: string; membraneProofs?: Record<string, MembraneProof>; } | (Optional) [HappBundleOptions](./tryorama.happbundleoptions.md) for the hApp bundle (optional). | +| options | [HappBundleOptions](./tryorama.happbundleoptions.md) | (Optional) [HappBundleOptions](./tryorama.happbundleoptions.md) for the hApp bundle (optional). | Returns: diff --git a/docs/tryorama.trycpconductor.md b/docs/tryorama.trycpconductor.md index 6aef75d9..df9b0b33 100644 --- a/docs/tryorama.trycpconductor.md +++ b/docs/tryorama.trycpconductor.md @@ -19,6 +19,13 @@ export declare class TryCpConductor implements IConductor | --- | --- | --- | | [(constructor)(tryCpClient, id)](./tryorama.trycpconductor._constructor_.md) | | Constructs a new instance of the TryCpConductor class | +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [id](./tryorama.trycpconductor.id.md) | | string | | +| [tryCpClient](./tryorama.trycpconductor.trycpclient.md) | | [TryCpClient](./tryorama.trycpclient.md) | | + ## Methods | Method | Modifiers | Description | @@ -33,6 +40,6 @@ export declare class TryCpConductor implements IConductor | [installAgentsHapps(options)](./tryorama.trycpconductor.installagentshapps.md) | | Install a set of DNAs for multiple agents into the conductor. | | [installHappBundle(appBundleSource, options)](./tryorama.trycpconductor.installhappbundle.md) | | Install a hApp bundle into the conductor. | | [saveDna(dnaContent)](./tryorama.trycpconductor.savedna.md) | | Upload a DNA file from the local file system to the server. | -| [shutDown()](./tryorama.trycpconductor.shutdown.md) | | Disconnect App interface and shut down the conductor. | +| [shutDown()](./tryorama.trycpconductor.shutdown.md) | | Disconnect app interface and shut down the conductor. | | [startUp(options)](./tryorama.trycpconductor.startup.md) | | Start a configured conductor. | diff --git a/docs/tryorama.trycpconductor.shutdown.md b/docs/tryorama.trycpconductor.shutdown.md index 4097a1fb..178635fa 100644 --- a/docs/tryorama.trycpconductor.shutdown.md +++ b/docs/tryorama.trycpconductor.shutdown.md @@ -4,7 +4,7 @@ ## TryCpConductor.shutDown() method -Disconnect App interface and shut down the conductor. +Disconnect app interface and shut down the conductor. Signature: diff --git a/docs/tryorama.trycpconductor.trycpclient.md b/docs/tryorama.trycpconductor.trycpclient.md new file mode 100644 index 00000000..1ca8581c --- /dev/null +++ b/docs/tryorama.trycpconductor.trycpclient.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [TryCpConductor](./tryorama.trycpconductor.md) > [tryCpClient](./tryorama.trycpconductor.trycpclient.md) + +## TryCpConductor.tryCpClient property + +Signature: + +```typescript +readonly tryCpClient: TryCpClient; +``` diff --git a/docs/tryorama.trycpconductoroptions.md b/docs/tryorama.trycpconductoroptions.md index 8469ff3a..49c25cdd 100644 --- a/docs/tryorama.trycpconductoroptions.md +++ b/docs/tryorama.trycpconductoroptions.md @@ -19,5 +19,4 @@ export interface TryCpConductorOptions | [logLevel?](./tryorama.trycpconductoroptions.loglevel.md) | [TryCpConductorLogLevel](./tryorama.trycpconductorloglevel.md) | (Optional) Log level of the conductor (optional).default: "info" | | [partialConfig?](./tryorama.trycpconductoroptions.partialconfig.md) | string | (Optional) Configuration for the conductor (optional). | | [startup?](./tryorama.trycpconductoroptions.startup.md) | boolean | (Optional) Start up conductor after creation.default: true | -| [timeout?](./tryorama.trycpconductoroptions.timeout.md) | number | (Optional) Timeout for requests to Admin and App API. | diff --git a/docs/tryorama.trycpconductoroptions.timeout.md b/docs/tryorama.trycpconductoroptions.timeout.md deleted file mode 100644 index 9ede6f33..00000000 --- a/docs/tryorama.trycpconductoroptions.timeout.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [TryCpConductorOptions](./tryorama.trycpconductoroptions.md) > [timeout](./tryorama.trycpconductoroptions.timeout.md) - -## TryCpConductorOptions.timeout property - -Timeout for requests to Admin and App API. - -Signature: - -```typescript -timeout?: number; -``` diff --git a/docs/tryorama.trycpscenario._constructor_.md b/docs/tryorama.trycpscenario._constructor_.md new file mode 100644 index 00000000..8179f109 --- /dev/null +++ b/docs/tryorama.trycpscenario._constructor_.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [TryCpScenario](./tryorama.trycpscenario.md) > [(constructor)](./tryorama.trycpscenario._constructor_.md) + +## TryCpScenario.(constructor) + +Constructs a new instance of the `TryCpScenario` class + +Signature: + +```typescript +constructor(); +``` diff --git a/docs/tryorama.trycpscenario.addclient.md b/docs/tryorama.trycpscenario.addclient.md new file mode 100644 index 00000000..734ee896 --- /dev/null +++ b/docs/tryorama.trycpscenario.addclient.md @@ -0,0 +1,27 @@ + + +[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [TryCpScenario](./tryorama.trycpscenario.md) > [addClient](./tryorama.trycpscenario.addclient.md) + +## TryCpScenario.addClient() method + +Creates a TryCP client connection and add it to the scenario. + +Signature: + +```typescript +addClient(serverUrl: URL, timeout?: number): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| serverUrl | URL | The TryCP server URL to connect to. | +| timeout | number | (Optional) An optional timeout for the web socket connection. | + +Returns: + +Promise<[TryCpClient](./tryorama.trycpclient.md)> + +The created TryCP client. + diff --git a/docs/tryorama.trycpscenario.addclientsplayers.md b/docs/tryorama.trycpscenario.addclientsplayers.md new file mode 100644 index 00000000..3062da06 --- /dev/null +++ b/docs/tryorama.trycpscenario.addclientsplayers.md @@ -0,0 +1,30 @@ + + +[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [TryCpScenario](./tryorama.trycpscenario.md) > [addClientsPlayers](./tryorama.trycpscenario.addclientsplayers.md) + +## TryCpScenario.addClientsPlayers() method + +Creates client connections for all passed in URLs and, depending on the options, creates multiple players with DNAs. Adds all clients to the scenario. + +Signature: + +```typescript +addClientsPlayers(serverUrls: URL[], options?: ClientsPlayersOptions): Promise<{ + client: TryCpClient; + players: TryCpPlayer[]; + }[]>; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| serverUrls | URL\[\] | The TryCP server URLs to connect to. | +| options | [ClientsPlayersOptions](./tryorama.clientsplayersoptions.md) | (Optional) [ClientsPlayersOptions](./tryorama.clientsplayersoptions.md) | + +Returns: + +Promise<{ client: [TryCpClient](./tryorama.trycpclient.md); players: [TryCpPlayer](./tryorama.trycpplayer.md)\[\]; }\[\]> + +The created TryCP clients and all conductors per client and all agents' hApps per conductor. + diff --git a/docs/tryorama.trycpscenario.addplayerswithhappbundles.md b/docs/tryorama.trycpscenario.addplayerswithhappbundles.md index e4bc20cf..59a85ed8 100644 --- a/docs/tryorama.trycpscenario.addplayerswithhappbundles.md +++ b/docs/tryorama.trycpscenario.addplayerswithhappbundles.md @@ -4,12 +4,12 @@ ## TryCpScenario.addPlayersWithHappBundles() method -Create and add multiple players to the scenario, with a hApp bundle installed for each player. +Creates and adds multiple players to the scenario, with a hApp bundle installed for each player. Signature: ```typescript -addPlayersWithHappBundles(playersHappBundles: Array<{ +addPlayersWithHappBundles(tryCpClient: TryCpClient, playersHappBundles: Array<{ appBundleSource: AppBundleSource; options?: HappBundleOptions & { signalHandler?: AppSignalCb; @@ -27,10 +27,12 @@ addPlayersWithHappBundles(playersHappBundles: Array<{ | Parameter | Type | Description | | --- | --- | --- | +| tryCpClient | [TryCpClient](./tryorama.trycpclient.md) | The client connection to the TryCP server on which to create the player. | | playersHappBundles | Array<{ appBundleSource: AppBundleSource; options?: [HappBundleOptions](./tryorama.happbundleoptions.md) & { signalHandler?: AppSignalCb; }; }> | An array with a hApp bundle for each player, and a signal handler (optional). | Returns: Promise<{ happId: string; agentPubKey: Uint8Array; cells: import("../../types.js").[CallableCell](./tryorama.callablecell.md)\[\]; namedCells: Map<string, import("../../types.js").[CallableCell](./tryorama.callablecell.md)>; conductor: [TryCpConductor](./tryorama.trycpconductor.md); }\[\]> +An array of the added players. diff --git a/docs/tryorama.trycpscenario.addplayerswithhapps.md b/docs/tryorama.trycpscenario.addplayerswithhapps.md index 5472ae29..194dd9f4 100644 --- a/docs/tryorama.trycpscenario.addplayerswithhapps.md +++ b/docs/tryorama.trycpscenario.addplayerswithhapps.md @@ -4,23 +4,24 @@ ## TryCpScenario.addPlayersWithHapps() method -Create and add multiple players to the scenario, with a set of DNAs installed for each player. +Creates and adds multiple players to the scenario, with a set of DNAs installed for each player. Signature: ```typescript -addPlayersWithHapps(agentHappOptions: AgentHappOptions[]): Promise; +addPlayersWithHapps(tryCpClient: TryCpClient, agentHappOptions: PlayerHappOptions[]): Promise; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| agentHappOptions | [AgentHappOptions](./tryorama.agenthappoptions.md)\[\] | [AgentHappOptions](./tryorama.agenthappoptions.md) for each player. | +| tryCpClient | [TryCpClient](./tryorama.trycpclient.md) | The client connection to the TryCP server on which to create the player. | +| agentHappOptions | [PlayerHappOptions](./tryorama.playerhappoptions.md)\[\] | [PlayerHappOptions](./tryorama.playerhappoptions.md) for each player. | Returns: Promise<[TryCpPlayer](./tryorama.trycpplayer.md)\[\]> -An array with the added players. +An array of the added players. diff --git a/docs/tryorama.trycpscenario.addplayerwithhapp.md b/docs/tryorama.trycpscenario.addplayerwithhapp.md index 64989b30..3ad2194f 100644 --- a/docs/tryorama.trycpscenario.addplayerwithhapp.md +++ b/docs/tryorama.trycpscenario.addplayerwithhapp.md @@ -4,23 +4,24 @@ ## TryCpScenario.addPlayerWithHapp() method -Create and add a single player to the scenario, with a set of DNAs installed. +Creates and adds a single player to the scenario, with a set of DNAs installed. Signature: ```typescript -addPlayerWithHapp(agentHappOptions: AgentHappOptions): Promise; +addPlayerWithHapp(tryCpClient: TryCpClient, playerHappOptions: PlayerHappOptions): Promise; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| agentHappOptions | [AgentHappOptions](./tryorama.agenthappoptions.md) | [AgentHappOptions](./tryorama.agenthappoptions.md). | +| tryCpClient | [TryCpClient](./tryorama.trycpclient.md) | The client connection to the TryCP server on which to create the player. | +| playerHappOptions | [PlayerHappOptions](./tryorama.playerhappoptions.md) | [PlayerHappOptions](./tryorama.playerhappoptions.md). | Returns: Promise<[TryCpPlayer](./tryorama.trycpplayer.md)> -A local player instance. +The created player instance. diff --git a/docs/tryorama.trycpscenario.addplayerwithhappbundle.md b/docs/tryorama.trycpscenario.addplayerwithhappbundle.md index ba2f74fb..f4fdeb50 100644 --- a/docs/tryorama.trycpscenario.addplayerwithhappbundle.md +++ b/docs/tryorama.trycpscenario.addplayerwithhappbundle.md @@ -4,12 +4,12 @@ ## TryCpScenario.addPlayerWithHappBundle() method -Create and add a single player to the scenario, with a hApp bundle installed. +Creates and adds a single player to the scenario, with a hApp bundle installed. Signature: ```typescript -addPlayerWithHappBundle(appBundleSource: AppBundleSource, options?: HappBundleOptions & { +addPlayerWithHappBundle(tryCpClient: TryCpClient, appBundleSource: AppBundleSource, options?: HappBundleOptions & { signalHandler?: AppSignalCb; }): Promise<{ happId: string; @@ -24,6 +24,7 @@ addPlayerWithHappBundle(appBundleSource: AppBundleSource, options?: HappBundleOp | Parameter | Type | Description | | --- | --- | --- | +| tryCpClient | [TryCpClient](./tryorama.trycpclient.md) | The client connection to the TryCP server on which to create the player. | | appBundleSource | AppBundleSource | The bundle or path to the bundle. | | options | [HappBundleOptions](./tryorama.happbundleoptions.md) & { signalHandler?: AppSignalCb; } | (Optional) [HappBundleOptions](./tryorama.happbundleoptions.md) plus a signal handler (optional). | @@ -31,5 +32,5 @@ addPlayerWithHappBundle(appBundleSource: AppBundleSource, options?: HappBundleOp Promise<{ happId: string; agentPubKey: Uint8Array; cells: import("../../types.js").[CallableCell](./tryorama.callablecell.md)\[\]; namedCells: Map<string, import("../../types.js").[CallableCell](./tryorama.callablecell.md)>; conductor: [TryCpConductor](./tryorama.trycpconductor.md); }> -A local player instance. +The created player instance. diff --git a/docs/tryorama.trycpscenario.cleanup.md b/docs/tryorama.trycpscenario.cleanup.md index bc13a0e6..479f6ac8 100644 --- a/docs/tryorama.trycpscenario.cleanup.md +++ b/docs/tryorama.trycpscenario.cleanup.md @@ -4,7 +4,7 @@ ## TryCpScenario.cleanUp() method -Shut down and delete all conductors in the scenario, and stop the TryCP server. +Shut down and delete all conductors and close all client connections in the scenario. Signature: diff --git a/docs/tryorama.trycpscenario.clients.md b/docs/tryorama.trycpscenario.clients.md new file mode 100644 index 00000000..67716b96 --- /dev/null +++ b/docs/tryorama.trycpscenario.clients.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [TryCpScenario](./tryorama.trycpscenario.md) > [clients](./tryorama.trycpscenario.clients.md) + +## TryCpScenario.clients property + +Signature: + +```typescript +clients: TryCpClient[]; +``` diff --git a/docs/tryorama.trycpscenario.create.md b/docs/tryorama.trycpscenario.create.md deleted file mode 100644 index e2ed266f..00000000 --- a/docs/tryorama.trycpscenario.create.md +++ /dev/null @@ -1,26 +0,0 @@ - - -[Home](./index.md) > [@holochain/tryorama](./tryorama.md) > [TryCpScenario](./tryorama.trycpscenario.md) > [create](./tryorama.trycpscenario.create.md) - -## TryCpScenario.create() method - -Factory method to create a new scenario. - -Signature: - -```typescript -static create(serverUrl: URL): Promise; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| serverUrl | URL | The URL of the TryCp server to connect to. | - -Returns: - -Promise<[TryCpScenario](./tryorama.trycpscenario.md)> - -A new scenario instance. - diff --git a/docs/tryorama.trycpscenario.md b/docs/tryorama.trycpscenario.md index e83271df..83b19c6c 100644 --- a/docs/tryorama.trycpscenario.md +++ b/docs/tryorama.trycpscenario.md @@ -4,33 +4,40 @@ ## TryCpScenario class -An abstraction of a test scenario to write tests against Holochain hApps, running on a TryCp conductor. +A test scenario abstraction with convenience functions to manage TryCP clients and players (agent + conductor). + +Clients in turn help manage conductors on TryCP servers. Clients can be added to a scenario to keep track of all server connections. When finishing a test scenario, all conductors of all clients can be easily cleaned up and the client connections closed. Signature: ```typescript -export declare class TryCpScenario implements IScenario +export declare class TryCpScenario ``` -Implements: [IScenario](./tryorama.iscenario.md) + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)()](./tryorama.trycpscenario._constructor_.md) | | Constructs a new instance of the TryCpScenario class | ## Properties | Property | Modifiers | Type | Description | | --- | --- | --- | --- | -| [conductors](./tryorama.trycpscenario.conductors.md) | | [TryCpConductor](./tryorama.trycpconductor.md)\[\] | | +| [clients](./tryorama.trycpscenario.clients.md) | | [TryCpClient](./tryorama.trycpclient.md)\[\] | | | [uid](./tryorama.trycpscenario.uid.md) | | string | | ## Methods | Method | Modifiers | Description | | --- | --- | --- | -| [addConductor(signalHandler)](./tryorama.trycpscenario.addconductor.md) | | Create and add a conductor to the scenario. | -| [addPlayersWithHappBundles(playersHappBundles)](./tryorama.trycpscenario.addplayerswithhappbundles.md) | | Create and add multiple players to the scenario, with a hApp bundle installed for each player. | -| [addPlayersWithHapps(agentHappOptions)](./tryorama.trycpscenario.addplayerswithhapps.md) | | Create and add multiple players to the scenario, with a set of DNAs installed for each player. | -| [addPlayerWithHapp(agentHappOptions)](./tryorama.trycpscenario.addplayerwithhapp.md) | | Create and add a single player to the scenario, with a set of DNAs installed. | -| [addPlayerWithHappBundle(appBundleSource, options)](./tryorama.trycpscenario.addplayerwithhappbundle.md) | | Create and add a single player to the scenario, with a hApp bundle installed. | -| [cleanUp()](./tryorama.trycpscenario.cleanup.md) | | Shut down and delete all conductors in the scenario, and stop the TryCP server. | -| [create(serverUrl)](./tryorama.trycpscenario.create.md) | static | Factory method to create a new scenario. | -| [shareAllAgents()](./tryorama.trycpscenario.shareallagents.md) | | Register all agents of all passed in conductors to each other. This skips peer discovery through gossip and thus accelerates test runs. | -| [shutDown()](./tryorama.trycpscenario.shutdown.md) | | Shut down all conductors in the scenario. | +| [addClient(serverUrl, timeout)](./tryorama.trycpscenario.addclient.md) | | Creates a TryCP client connection and add it to the scenario. | +| [addClientsPlayers(serverUrls, options)](./tryorama.trycpscenario.addclientsplayers.md) | | Creates client connections for all passed in URLs and, depending on the options, creates multiple players with DNAs. Adds all clients to the scenario. | +| [addPlayersWithHappBundles(tryCpClient, playersHappBundles)](./tryorama.trycpscenario.addplayerswithhappbundles.md) | | Creates and adds multiple players to the scenario, with a hApp bundle installed for each player. | +| [addPlayersWithHapps(tryCpClient, agentHappOptions)](./tryorama.trycpscenario.addplayerswithhapps.md) | | Creates and adds multiple players to the scenario, with a set of DNAs installed for each player. | +| [addPlayerWithHapp(tryCpClient, playerHappOptions)](./tryorama.trycpscenario.addplayerwithhapp.md) | | Creates and adds a single player to the scenario, with a set of DNAs installed. | +| [addPlayerWithHappBundle(tryCpClient, appBundleSource, options)](./tryorama.trycpscenario.addplayerwithhappbundle.md) | | Creates and adds a single player to the scenario, with a hApp bundle installed. | +| [cleanUp()](./tryorama.trycpscenario.cleanup.md) | | Shut down and delete all conductors and close all client connections in the scenario. | +| [shareAllAgents()](./tryorama.trycpscenario.shareallagents.md) | | Registers all agents of all passed in conductors to each other. This skips peer discovery through gossip and thus accelerates test runs. | +| [shutDown()](./tryorama.trycpscenario.shutdown.md) | | Shut down all conductors of all clients in the scenario. | diff --git a/docs/tryorama.trycpscenario.shareallagents.md b/docs/tryorama.trycpscenario.shareallagents.md index 009db1e5..7b868838 100644 --- a/docs/tryorama.trycpscenario.shareallagents.md +++ b/docs/tryorama.trycpscenario.shareallagents.md @@ -4,7 +4,7 @@ ## TryCpScenario.shareAllAgents() method -Register all agents of all passed in conductors to each other. This skips peer discovery through gossip and thus accelerates test runs. +Registers all agents of all passed in conductors to each other. This skips peer discovery through gossip and thus accelerates test runs. Signature: diff --git a/docs/tryorama.trycpscenario.shutdown.md b/docs/tryorama.trycpscenario.shutdown.md index 2c6e7a75..ab34d480 100644 --- a/docs/tryorama.trycpscenario.shutdown.md +++ b/docs/tryorama.trycpscenario.shutdown.md @@ -4,7 +4,7 @@ ## TryCpScenario.shutDown() method -Shut down all conductors in the scenario. +Shut down all conductors of all clients in the scenario. Signature: diff --git a/package-lock.json b/package-lock.json index 53a3590c..2ed3b209 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@holochain/tryorama", - "version": "0.5.9", + "version": "0.6.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@holochain/tryorama", - "version": "0.5.9", + "version": "0.6.1", "license": "CAL-1.0", "workspaces": [ "crates/trycp_server/test" diff --git a/package.json b/package.json index 4abe7ec7..b7279bf9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@holochain/tryorama", "description": "Toolset to manage Holochain conductors and facilitate running test scenarios", - "version": "0.6.0", + "version": "0.6.2", "author": "Holochain Foundation", "keywords": [ "holochain", @@ -39,8 +39,8 @@ "test:local": "npm run test:local:conductor && npm run test:local:scenario", "test:local:conductor": "node --loader ts-node/esm ts/test/local/conductor.ts", "test:local:scenario": "node --loader ts-node/esm ts/test/local/scenario.ts", - "test:trycp": "npm run test:trycp:server && npm run test:trycp:conductor && npm run test:trycp:scenario", - "test:trycp:server": "node --loader ts-node/esm ts/test/trycp/server.ts", + "test:trycp": "npm run test:trycp:client && npm run test:trycp:conductor && npm run test:trycp:scenario", + "test:trycp:client": "node --loader ts-node/esm ts/test/trycp/client.ts", "test:trycp:conductor": "node --loader ts-node/esm ts/test/trycp/conductor.ts", "test:trycp:scenario": "node --loader ts-node/esm ts/test/trycp/scenario.ts" }, diff --git a/run-test.sh b/run-test.sh index b942b0e1..8caf133c 100755 --- a/run-test.sh +++ b/run-test.sh @@ -6,9 +6,11 @@ cargo build --release --target-dir target cd ../.. # build test hApp -cd ts/test/fixture/zomes/entry +cd ts/test/fixture/zomes/integrity cargo build --release --target wasm32-unknown-unknown -cd ../../ # into fixtures +cd ../coordinator +cargo build --release --target wasm32-unknown-unknown +cd ../.. # into fixtures hc dna pack . -o entry.dna hc app pack . -o entry.happ cd ../../.. diff --git a/ts/src/local/conductor.ts b/ts/src/local/conductor.ts index 00862567..5ab76bd7 100644 --- a/ts/src/local/conductor.ts +++ b/ts/src/local/conductor.ts @@ -4,8 +4,6 @@ import { AppSignalCb, AppWebsocket, AttachAppInterfaceRequest, - DnaSource, - DnaProperties, InstallAppBundleRequest, InstallAppDnaPayload, RegisterDnaRequest, @@ -21,6 +19,7 @@ import { enableAndGetAgentHapp } from "../common.js"; import { makeLogger } from "../logger.js"; import { AgentHapp, + AgentsHappsOptions, HappBundleOptions, IConductor, _RegisterDnaReqOpts, @@ -399,48 +398,57 @@ export class Conductor implements IConductor { * 2-dimensional array, and a UID for the DNAs (optional). * @returns An array with each agent's hApps. */ - async installAgentsHapps(options: { - agentsDnas: DnaSource[][]; - uid?: string; - properties?: DnaProperties; - }) { + async installAgentsHapps(options: AgentsHappsOptions) { const agentsHapps: AgentHapp[] = []; + const agentsDnas = Array.isArray(options) ? options : options.agentsDnas; - for (const agent of options.agentsDnas) { - const dnas: InstallAppDnaPayload[] = []; - const agentPubKey = await this.adminWs().generateAgentPubKey(); - const appId = `app-${uuidv4()}`; + for (const agentDnas of agentsDnas) { + const dnasToInstall: InstallAppDnaPayload[] = []; + const appId = + ("installedAppId" in options && options.installedAppId) || + `app-${uuidv4()}`; + const agentPubKey = + ("agentPubKey" in agentDnas && agentDnas.agentPubKey) || + (await this.adminWs().generateAgentPubKey()); - for (const dna of agent) { - let role_id: string; + const dnas = "dnas" in agentDnas ? agentDnas.dnas : agentDnas; + for (const dna of dnas) { + let roleId: string; const registerDnaReqOpts: _RegisterDnaReqOpts = { - uid: options.uid, - properties: options.properties, + uid: ("uid" in options && options.uid) || undefined, + properties: ("properties" in dna && dna.properties) || undefined, }; - if ("path" in dna) { - registerDnaReqOpts["path"] = dna.path; - role_id = `${dna.path}-${uuidv4()}`; - } else if ("hash" in dna) { - registerDnaReqOpts["hash"] = dna.hash; - role_id = `dna-${uuidv4()}`; + const dnaSource = "source" in dna ? dna.source : dna; + if ("path" in dnaSource) { + registerDnaReqOpts.path = dnaSource.path; + roleId = `${dnaSource.path}-${uuidv4()}`; + } else if ("hash" in dnaSource) { + registerDnaReqOpts.hash = dnaSource.hash; + roleId = `dna-${uuidv4()}`; } else { - registerDnaReqOpts["bundle"] = dna.bundle; - role_id = `${dna.bundle.manifest.name}-${uuidv4()}`; + registerDnaReqOpts.bundle = dnaSource.bundle; + roleId = `${dnaSource.bundle.manifest.name}-${uuidv4()}`; } const dnaHash = await this.adminWs().registerDna( registerDnaReqOpts as RegisterDnaRequest ); - dnas.push({ hash: dnaHash, role_id }); + const membrane_proof = + "membraneProof" in dna ? dna.membraneProof : undefined; + dnasToInstall.push({ + hash: dnaHash, + role_id: ("roleId" in dna && dna.roleId) || roleId, + membrane_proof, + }); } const installedAppInfo = await this.adminWs().installApp({ installed_app_id: appId, agent_key: agentPubKey, - dnas, + dnas: dnasToInstall, }); const agentHapp = await enableAndGetAgentHapp( this, diff --git a/ts/src/local/scenario.ts b/ts/src/local/scenario.ts index c2379af2..41d0530b 100644 --- a/ts/src/local/scenario.ts +++ b/ts/src/local/scenario.ts @@ -1,13 +1,13 @@ +import { AppBundleSource, AppSignalCb } from "@holochain/client"; import { v4 as uuidv4 } from "uuid"; -import { AppBundleSource, AppSignalCb, DnaSource } from "@holochain/client"; -import { cleanAllConductors, createConductor, Conductor } from "./conductor.js"; +import { addAllAgentsToAllConductors } from "../common.js"; import { - AgentHappOptions, + AgentDnas, HappBundleOptions, IPlayer, - IScenario, + PlayerHappOptions, } from "../types.js"; -import { addAllAgentsToAllConductors } from "../common.js"; +import { cleanAllConductors, Conductor, createConductor } from "./conductor.js"; /** * A player tied to a {@link Conductor}. @@ -34,7 +34,7 @@ export interface ScenarioOptions { * * @public */ -export class Scenario implements IScenario { +export class Scenario { private timeout: number | undefined; uid: string; conductors: Conductor[]; @@ -69,24 +69,26 @@ export class Scenario implements IScenario { * Create and add a single player to the scenario, with a set of DNAs * installed. * - * @param agentHappOptions - {@link AgentHappOptions}. + * @param playerHappOptions - {@link PlayerHappOptions}. * @returns A local player instance. */ - async addPlayerWithHapp(agentHappOptions: AgentHappOptions): Promise { - const signalHandler = Array.isArray(agentHappOptions) - ? undefined - : agentHappOptions.signalHandler; - const properties = Array.isArray(agentHappOptions) + async addPlayerWithHapp( + playerHappOptions: PlayerHappOptions + ): Promise { + const signalHandler = Array.isArray(playerHappOptions) ? undefined - : agentHappOptions.properties; - const agentsDnas: DnaSource[][] = Array.isArray(agentHappOptions) - ? [agentHappOptions] - : [agentHappOptions.dnas]; + : playerHappOptions.signalHandler; + const agentsDnas: AgentDnas[] = [ + { + dnas: Array.isArray(playerHappOptions) + ? playerHappOptions.map((dnaSource) => ({ source: dnaSource })) + : playerHappOptions.dnas, + }, + ]; const conductor = await this.addConductor(signalHandler); const [agentHapp] = await conductor.installAgentsHapps({ agentsDnas, uid: this.uid, - properties, }); return { conductor, ...agentHapp }; } @@ -95,11 +97,11 @@ export class Scenario implements IScenario { * Create and add multiple players to the scenario, with a set of DNAs * installed for each player. * - * @param agentHappOptions - {@link AgentHappOptions} for each player. + * @param agentHappOptions - {@link PlayerHappOptions} for each player. * @returns An array with the added players. */ async addPlayersWithHapps( - agentHappOptions: AgentHappOptions[] + agentHappOptions: PlayerHappOptions[] ): Promise { const players = await Promise.all( agentHappOptions.map((options) => this.addPlayerWithHapp(options)) diff --git a/ts/src/trycp/conductor/conductor.ts b/ts/src/trycp/conductor/conductor.ts index 30276e65..266d584b 100644 --- a/ts/src/trycp/conductor/conductor.ts +++ b/ts/src/trycp/conductor/conductor.ts @@ -9,7 +9,6 @@ import { CreateCloneCellRequest, DisableAppRequest, DnaHash, - DnaProperties, DnaSource, DumpFullStateRequest, DumpStateRequest, @@ -19,7 +18,6 @@ import { InstallAppDnaPayload, InstallAppRequest, ListAppsRequest, - MembraneProof, RegisterDnaRequest, RequestAgentInfoRequest, StartAppRequest, @@ -32,7 +30,13 @@ import { URL } from "node:url"; import { v4 as uuidv4 } from "uuid"; import { enableAndGetAgentHapp } from "../../common.js"; import { makeLogger } from "../../logger.js"; -import { AgentHapp, IConductor, _RegisterDnaReqOpts } from "../../types.js"; +import { + AgentHapp, + HappBundleOptions, + IConductor, + AgentsHappsOptions, + _RegisterDnaReqOpts, +} from "../../types.js"; import { TryCpClient, TryCpConductorLogLevel } from "../index.js"; import { RequestAdminInterfaceData, @@ -86,27 +90,23 @@ export interface TryCpConductorOptions { * default: "info" */ logLevel?: TryCpConductorLogLevel; - - /** - * Timeout for requests to Admin and App API. - */ - timeout?: number; } /** - * The function to create a TryCP Conductor (aka "Player"). + * The function to create a TryCP Conductor. By default configures and starts + * it. * - * @param serverUrl - The URL of the TryCP server to connect to. - * @returns A configured Conductor instance. + * @param tryCpClient - The client connection to the TryCP server on which to + * create the conductor. + * @returns A conductor instance. * * @public */ export const createTryCpConductor = async ( - serverUrl: URL, + tryCpClient: TryCpClient, options?: TryCpConductorOptions ) => { - const client = await TryCpClient.create(serverUrl, options?.timeout); - const conductor = new TryCpConductor(client, options?.id); + const conductor = new TryCpConductor(tryCpClient, options?.id); if (options?.startup !== false) { // configure and startup conductor by default await conductor.configure(options?.partialConfig); @@ -121,9 +121,9 @@ export const createTryCpConductor = async ( * @public */ export class TryCpConductor implements IConductor { - private readonly id: string; - private readonly tryCpClient: TryCpClient; + readonly id: string; private appInterfacePort: undefined | number; + readonly tryCpClient: TryCpClient; constructor(tryCpClient: TryCpClient, id?: ConductorId) { this.tryCpClient = tryCpClient; @@ -165,7 +165,7 @@ export class TryCpConductor implements IConductor { } /** - * Disconnect App interface and shut down the conductor. + * Disconnect app interface and shut down the conductor. * * @returns An empty success response. * @@ -636,39 +636,41 @@ export class TryCpConductor implements IConductor { /** * Install a set of DNAs for multiple agents into the conductor. * - * @param options - An array of DNAs for each agent, resulting in a - * 2-dimensional array, and a UID for the DNAs (optional). + * @param options - {@link AgentsHappsOptions} * @returns An array with each agent's hApp. */ - async installAgentsHapps(options: { - agentsDnas: DnaSource[][]; - signalHandler?: AppSignalCb; - uid?: string; - properties?: DnaProperties; - }) { + async installAgentsHapps(options: AgentsHappsOptions) { const agentsHapps: AgentHapp[] = []; - - for (const agentDnas of options.agentsDnas) { - const dnas: InstallAppDnaPayload[] = []; - const agentPubKey = await this.adminWs().generateAgentPubKey(); - const appId = `app-${uuidv4()}`; + const agentsDnas = Array.isArray(options) ? options : options.agentsDnas; + + for (const agentDnas of agentsDnas) { + const dnasToInstall: InstallAppDnaPayload[] = []; + const appId = + ("installedAppId" in options && options.installedAppId) || + `app-${uuidv4()}`; + const agentPubKey = + ("agentPubKey" in agentDnas && agentDnas.agentPubKey) || + (await this.adminWs().generateAgentPubKey()); logger.debug( `installing app with id ${appId} for agent ${Buffer.from( agentPubKey ).toString("base64")}` ); - for (const dna of agentDnas) { - let role_id: string; + const dnas = "dnas" in agentDnas ? agentDnas.dnas : agentDnas; + for (const dna of dnas) { + let roleId: string; const registerDnaReqOpts: _RegisterDnaReqOpts = { - uid: options.uid, - properties: options.properties, + uid: ("uid" in options && options.uid) || undefined, + properties: ("properties" in dna && dna.properties) || undefined, }; - if ("path" in dna) { + const dnaSource = "source" in dna ? dna.source : dna; + if ("path" in dnaSource) { + const path = dnaSource.path; const dnaContent = await new Promise((resolve, reject) => { - fs.readFile(dna.path, null, (err, data) => { + fs.readFile(path, null, (err, data) => { if (err) { reject(err); } @@ -676,27 +678,33 @@ export class TryCpConductor implements IConductor { }); }); const relativePath = await this.saveDna(dnaContent); - registerDnaReqOpts["path"] = relativePath; - role_id = `${dna.path}-${uuidv4()}`; - } else if ("hash" in dna) { - registerDnaReqOpts["hash"] = dna.hash; - role_id = `dna-${uuidv4()}`; + registerDnaReqOpts.path = relativePath; + roleId = `${path}-${uuidv4()}`; + } else if ("hash" in dnaSource) { + registerDnaReqOpts.hash = dnaSource.hash; + roleId = `dna-${uuidv4()}`; } else { - registerDnaReqOpts["bundle"] = dna.bundle; - role_id = `${dna.bundle.manifest.name}-${uuidv4()}`; + registerDnaReqOpts.bundle = dnaSource.bundle; + roleId = `${dnaSource.bundle.manifest.name}-${uuidv4()}`; } const dnaHash = await this.adminWs().registerDna( registerDnaReqOpts as RegisterDnaRequest ); - dnas.push({ hash: dnaHash, role_id }); + const membrane_proof = + "membraneProof" in dna ? dna.membraneProof : undefined; + dnasToInstall.push({ + hash: dnaHash, + role_id: ("roleId" in dna && dna.roleId) || roleId, + membrane_proof, + }); } const installedAppInfo = await this.adminWs().installApp({ installed_app_id: appId, agent_key: agentPubKey, - dnas, + dnas: dnasToInstall, }); const agentHapp = await enableAndGetAgentHapp( @@ -719,12 +727,7 @@ export class TryCpConductor implements IConductor { */ async installHappBundle( appBundleSource: AppBundleSource, - options?: { - agentPubKey?: AgentPubKey; - installedAppId?: string; - uid?: string; - membraneProofs?: Record; - } + options?: HappBundleOptions ) { const agentPubKey = options?.agentPubKey ?? (await this.adminWs().generateAgentPubKey()); @@ -749,19 +752,3 @@ export class TryCpConductor implements IConductor { return agentHapp; } } - -/** - * Run the `reset` command on the TryCP server to delete all conductor data. - * - * @param serverUrl - The TryCP server to connect to. - * @returns An empty success response. - * - * @public - */ -export const cleanAllTryCpConductors = async (serverUrl: URL) => { - const client = await TryCpClient.create(serverUrl); - const response = await client.call({ type: "reset" }); - assert(response === TRYCP_SUCCESS_RESPONSE); - await client.close(); - return response; -}; diff --git a/ts/src/trycp/conductor/scenario.ts b/ts/src/trycp/conductor/scenario.ts index 3a234f61..4e5dcbe5 100644 --- a/ts/src/trycp/conductor/scenario.ts +++ b/ts/src/trycp/conductor/scenario.ts @@ -1,28 +1,52 @@ -import { AppBundleSource, AppSignalCb, DnaSource } from "@holochain/client"; +import { AgentPubKey, AppBundleSource, AppSignalCb } from "@holochain/client"; import { URL } from "url"; import { v4 as uuidv4 } from "uuid"; import { addAllAgentsToAllConductors as shareAllAgents } from "../../common.js"; import { - AgentHappOptions, + AgentDnas, + Dna, HappBundleOptions, IPlayer, - IScenario, + PlayerHappOptions, } from "../../types.js"; -import { TryCpServer } from "../trycp-server.js"; -import { - cleanAllTryCpConductors, - createTryCpConductor, - TryCpConductor, -} from "./conductor.js"; - -const partialConfig = `signing_service_uri: ~ -encryption_service_uri: ~ -decryption_service_uri: ~ -dpki: ~ -network: - transport_pool: - - type: quic - network_type: quic_mdns`; +import { TryCpClient } from "../trycp-client.js"; +import { TryCpConductor } from "./conductor.js"; + +/** + * @public + */ +export interface ClientsPlayersOptions { + /** + * A timeout for the web socket connection (optional). + */ + clientTimeout?: number; + + /** + * An array of DNAs that will be installed for each agent (optional). + */ + dnas?: Dna[]; + + /** + * A list of previously generated agent pub keys (optional). + */ + agentPubKeys?: AgentPubKey[]; + + /** + * Number of conductors per client. Default to 1. + */ + numberOfConductorsPerClient?: number; + + /** + * Number of agents per conductor. Defaults to 1. Requires `dnas` to be + * specified. + */ + numberOfAgentsPerConductor?: number; + + /** + * A signal handler to be registered in conductors. + */ + signalHandler?: AppSignalCb; +} /** * A player tied to a {@link TryCpConductor}. @@ -34,111 +58,175 @@ export interface TryCpPlayer extends IPlayer { } /** - * An abstraction of a test scenario to write tests against Holochain hApps, - * running on a TryCp conductor. + * A test scenario abstraction with convenience functions to manage TryCP + * clients and players (agent + conductor). + * + * Clients in turn help manage conductors on TryCP servers. Clients can be + * added to a scenario to keep track of all server connections. When finishing + * a test scenario, all conductors of all clients can be easily cleaned up and + * the client connections closed. * * @public */ -export class TryCpScenario implements IScenario { +export class TryCpScenario { uid: string; - conductors: TryCpConductor[]; - private serverUrl: URL; - private server: TryCpServer | undefined; + clients: TryCpClient[]; - private constructor(serverUrl: URL) { + constructor() { this.uid = uuidv4(); - this.conductors = []; - this.serverUrl = serverUrl; - this.server = undefined; + this.clients = []; } /** - * Factory method to create a new scenario. + * Creates a TryCP client connection and add it to the scenario. * - * @param serverUrl - The URL of the TryCp server to connect to. - * @returns A new scenario instance. + * @param serverUrl - The TryCP server URL to connect to. + * @param timeout - An optional timeout for the web socket connection. + * @returns The created TryCP client. */ - static async create(serverUrl: URL) { - const scenario = new TryCpScenario(serverUrl); - scenario.server = await TryCpServer.start(); - return scenario; + async addClient(serverUrl: URL, timeout?: number) { + const client = await TryCpClient.create(serverUrl, timeout); + this.clients.push(client); + return client; } /** - * Create and add a conductor to the scenario. + * Creates client connections for all passed in URLs and, depending on the + * options, creates multiple players with DNAs. Adds all clients to the + * scenario. * - * @param signalHandler - A callback function to handle signals. - * @returns The newly added conductor instance. + * @param serverUrls - The TryCP server URLs to connect to. + * @param options - {@link ClientsPlayersOptions} + * @returns The created TryCP clients and all conductors per client and all + * agents' hApps per conductor. */ - async addConductor(signalHandler?: AppSignalCb) { - const conductor = await createTryCpConductor(this.serverUrl, { - partialConfig, - }); - await conductor.adminWs().attachAppInterface(); - await conductor.connectAppInterface(signalHandler); - this.conductors.push(conductor); - return conductor; + async addClientsPlayers(serverUrls: URL[], options?: ClientsPlayersOptions) { + const clientsPlayers: Array<{ + client: TryCpClient; + players: TryCpPlayer[]; + }> = []; + + // create client connections for specified URLs + for (const serverUrl of serverUrls) { + const client = await this.addClient(serverUrl, options?.clientTimeout); + const players: TryCpPlayer[] = []; + const numberOfConductorsPerClient = + options?.numberOfConductorsPerClient ?? 1; + + // create conductors for each client + for (let i = 0; i < numberOfConductorsPerClient; i++) { + const conductor = await client.addConductor(options?.signalHandler); + const numberOfAgentsPerConductor = + options?.numberOfAgentsPerConductor ?? 1; + + if (options?.numberOfAgentsPerConductor) { + // install agents hApps for each conductor + if (options.dnas === undefined) { + throw new Error("no DNAs specified to be installed for agents"); + } + + // TS fails to infer that options.dnas cannot be `undefined` here + const dnas = options.dnas; + + let agentsDnas: AgentDnas[]; + if (options.agentPubKeys) { + if (options.agentPubKeys.length !== options.dnas.length) { + throw new Error( + "number of agent pub keys doesn't match number of DNAs" + ); + } + agentsDnas = options.agentPubKeys.map((agentPubKey) => ({ + agentPubKey, + dnas, + })); + } else { + agentsDnas = [...Array(numberOfAgentsPerConductor)].map(() => ({ + dnas, + })); + } + + const installedAgentsHapps = await conductor.installAgentsHapps({ + agentsDnas, + }); + installedAgentsHapps.forEach((agentHapps) => + players.push({ conductor, ...agentHapps }) + ); + } + } + clientsPlayers.push({ client, players }); + } + return clientsPlayers; } /** - * Create and add a single player to the scenario, with a set of DNAs + * Creates and adds a single player to the scenario, with a set of DNAs * installed. * - * @param agentHappOptions - {@link AgentHappOptions}. - * @returns A local player instance. + * @param tryCpClient - The client connection to the TryCP server on which to + * create the player. + * @param playerHappOptions - {@link PlayerHappOptions}. + * @returns The created player instance. */ async addPlayerWithHapp( - agentHappOptions: AgentHappOptions + tryCpClient: TryCpClient, + playerHappOptions: PlayerHappOptions ): Promise { - const signalHandler = Array.isArray(agentHappOptions) - ? undefined - : agentHappOptions.signalHandler; - const properties = Array.isArray(agentHappOptions) + const signalHandler = Array.isArray(playerHappOptions) ? undefined - : agentHappOptions.properties; - const agentsDnas: DnaSource[][] = Array.isArray(agentHappOptions) - ? [agentHappOptions] - : [agentHappOptions.dnas]; - const conductor = await this.addConductor(signalHandler); + : playerHappOptions.signalHandler; + const agentsDnas: AgentDnas[] = [ + { + dnas: Array.isArray(playerHappOptions) + ? playerHappOptions.map((dnaSource) => ({ source: dnaSource })) + : playerHappOptions.dnas, + }, + ]; + const conductor = await tryCpClient.addConductor(signalHandler); const [agentHapp] = await conductor.installAgentsHapps({ agentsDnas, uid: this.uid, - properties, - signalHandler, }); return { conductor, ...agentHapp }; } /** - * Create and add multiple players to the scenario, with a set of DNAs + * Creates and adds multiple players to the scenario, with a set of DNAs * installed for each player. * - * @param agentHappOptions - {@link AgentHappOptions} for each player. - * @returns An array with the added players. + * @param tryCpClient - The client connection to the TryCP server on which to + * create the player. + * @param agentHappOptions - {@link PlayerHappOptions} for each player. + * @returns An array of the added players. */ async addPlayersWithHapps( - agentHappOptions: AgentHappOptions[] + tryCpClient: TryCpClient, + agentHappOptions: PlayerHappOptions[] ): Promise { const players = await Promise.all( - agentHappOptions.map((options) => this.addPlayerWithHapp(options)) + agentHappOptions.map((options) => + this.addPlayerWithHapp(tryCpClient, options) + ) ); return players; } /** - * Create and add a single player to the scenario, with a hApp bundle + * Creates and adds a single player to the scenario, with a hApp bundle * installed. * + * @param tryCpClient - The client connection to the TryCP server on which to + * create the player. * @param appBundleSource - The bundle or path to the bundle. * @param options - {@link HappBundleOptions} plus a signal handler * (optional). - * @returns A local player instance. + * @returns The created player instance. */ async addPlayerWithHappBundle( + tryCpClient: TryCpClient, appBundleSource: AppBundleSource, options?: HappBundleOptions & { signalHandler?: AppSignalCb } ) { - const conductor = await this.addConductor(options?.signalHandler); + const conductor = await tryCpClient.addConductor(options?.signalHandler); options = options ? Object.assign(options, { uid: options.uid ?? this.uid }) : { uid: this.uid }; @@ -146,19 +234,21 @@ export class TryCpScenario implements IScenario { appBundleSource, options ); - this.conductors.push(conductor); return { conductor, ...agentHapp }; } /** - * Create and add multiple players to the scenario, with a hApp bundle + * Creates and adds multiple players to the scenario, with a hApp bundle * installed for each player. * + * @param tryCpClient - The client connection to the TryCP server on which to + * create the player. * @param playersHappBundles - An array with a hApp bundle for each player, * and a signal handler (optional). - * @returns + * @returns An array of the added players. */ async addPlayersWithHappBundles( + tryCpClient: TryCpClient, playersHappBundles: Array<{ appBundleSource: AppBundleSource; options?: HappBundleOptions & { signalHandler?: AppSignalCb }; @@ -167,6 +257,7 @@ export class TryCpScenario implements IScenario { const players = await Promise.all( playersHappBundles.map(async (playerHappBundle) => this.addPlayerWithHappBundle( + tryCpClient, playerHappBundle.appBundleSource, playerHappBundle.options ) @@ -176,35 +267,30 @@ export class TryCpScenario implements IScenario { } /** - * Register all agents of all passed in conductors to each other. This skips + * Registers all agents of all passed in conductors to each other. This skips * peer discovery through gossip and thus accelerates test runs. - * - * @public */ async shareAllAgents() { - return shareAllAgents(this.conductors); + return shareAllAgents( + this.clients.map((client) => client.conductors).flat() + ); } /** - * Shut down all conductors in the scenario. + * Shut down all conductors of all clients in the scenario. */ async shutDown() { - await Promise.all(this.conductors.map((conductor) => conductor.shutDown())); await Promise.all( - this.conductors.map((conductor) => conductor.disconnectClient()) + this.clients.map((client) => client.shutDownConductors()) ); } /** - * Shut down and delete all conductors in the scenario, and stop the TryCP - * server. - * - * @public + * Shut down and delete all conductors and close all client connections in + * the scenario. */ async cleanUp() { - await this.shutDown(); - await cleanAllTryCpConductors(this.serverUrl); - this.conductors = []; - await this.server?.stop(); + await Promise.all(this.clients.map((client) => client.cleanUp())); + this.clients = []; } } diff --git a/ts/src/trycp/trycp-client.ts b/ts/src/trycp/trycp-client.ts index 96fc9bf8..34ec5237 100644 --- a/ts/src/trycp/trycp-client.ts +++ b/ts/src/trycp/trycp-client.ts @@ -1,9 +1,14 @@ import { AppSignalCb } from "@holochain/client"; import msgpack from "@msgpack/msgpack"; import cloneDeep from "lodash/cloneDeep.js"; +import assert from "node:assert"; import { URL } from "node:url"; import { WebSocket } from "ws"; import { makeLogger } from "../logger.js"; +import { + createTryCpConductor as createConductor, + TryCpConductor, +} from "./conductor/conductor.js"; import { TryCpApiResponse, TryCpRequest, @@ -22,9 +27,21 @@ import { const logger = makeLogger("TryCP client"); let requestId = 0; +const partialConfig = `signing_service_uri: ~ +encryption_service_uri: ~ +decryption_service_uri: ~ +dpki: ~ +network: + transport_pool: + - type: quic + network_type: quic_mdns`; + /** * A factory class to create client connections to a running TryCP server. * + * With a client, conductors on the server can ba configured, started and + * stopped. All valid Admin and App API commands can be sent to the server too. + * * @public */ export class TryCpClient { @@ -37,21 +54,20 @@ export class TryCpClient { }; private signalHandlers: Record; + conductors: TryCpConductor[]; + private constructor(serverUrl: URL, timeout = 15000) { this.ws = new WebSocket(serverUrl, { timeout }); this.requestPromises = {}; this.signalHandlers = {}; - } - - setSignalHandler(port: number, signalHandler?: AppSignalCb) { - this.signalHandlers[port] = signalHandler; + this.conductors = []; } /** * Create a client connection to a running TryCP server. * * @param serverUrl - The URL of the TryCP server. - * @returns A client connection. + * @returns The created client connection. */ static async create(serverUrl: URL, timeout?: number) { const tryCpClient = new TryCpClient(serverUrl, timeout); @@ -134,27 +150,8 @@ export class TryCpClient { return connectPromise; } - private processSuccessResponse(response: _TryCpSuccessResponseSeralized) { - if (response === TRYCP_SUCCESS_RESPONSE || typeof response === "string") { - logger.debug(`response ${JSON.stringify(response, null, 4)}\n`); - return response; - } - - const deserializedApiResponse = deserializeApiResponse(response); - - // when the request fails, the response's type is "error" - if (deserializedApiResponse.type === "error") { - const errorMessage = `error response from Admin API\n${JSON.stringify( - deserializedApiResponse.data, - null, - 4 - )}`; - throw new Error(errorMessage); - } - - logger.debug(this.getFormattedResponseLog(deserializedApiResponse)); - - return deserializedApiResponse; + setSignalHandler(port: number, signalHandler?: AppSignalCb) { + this.signalHandlers[port] = signalHandler; } /** @@ -238,6 +235,71 @@ export class TryCpClient { return callPromise; } + /** + * Create and add a conductor to the client. + * + * @param signalHandler - A callback function to handle signals. + * @returns The newly added conductor instance. + */ + async addConductor(signalHandler?: AppSignalCb) { + const conductor = await createConductor(this, { partialConfig }); + await conductor.adminWs().attachAppInterface(); + await conductor.connectAppInterface(signalHandler); + this.conductors.push(conductor); + return conductor; + } + + /** + * Shut down all conductors on the connected TryCP server and disconnect + * their app interfaces. + */ + async shutDownConductors() { + await Promise.all(this.conductors.map((conductor) => conductor.shutDown())); + } + + /** + * Run the `reset` command on the TryCP server to delete all conductor data. + * + * @returns An empty success response. + */ + async cleanAllConductors() { + const response = await this.call({ type: "reset" }); + assert(response === TRYCP_SUCCESS_RESPONSE); + return response; + } + + /** + * Shut down all registered conductors and delete them, and close the client + * connection. + */ + async cleanUp() { + await this.cleanAllConductors(); + await this.close(); + } + + private processSuccessResponse(response: _TryCpSuccessResponseSeralized) { + if (response === TRYCP_SUCCESS_RESPONSE || typeof response === "string") { + logger.debug(`response ${JSON.stringify(response, null, 4)}\n`); + return response; + } + + const deserializedApiResponse = deserializeApiResponse(response); + + // when the request fails, the response's type is "error" + if (deserializedApiResponse.type === "error") { + const errorMessage = `error response from Admin API\n${JSON.stringify( + deserializedApiResponse.data, + null, + 4 + )}`; + throw new Error(errorMessage); + } + + logger.debug(this.getFormattedResponseLog(deserializedApiResponse)); + + return deserializedApiResponse; + } + private getFormattedResponseLog(response: TryCpApiResponse) { let debugLog; if ( diff --git a/ts/src/trycp/trycp-server.ts b/ts/src/trycp/trycp-server.ts index f485c271..030c7f48 100644 --- a/ts/src/trycp/trycp-server.ts +++ b/ts/src/trycp/trycp-server.ts @@ -57,9 +57,7 @@ export class TryCpServer { logger.error(data); return; } - const regexServerStarted = new RegExp( - `Listening on ${TRYCP_SERVER_HOST}:${TRYCP_SERVER_PORT}` - ); + const regexServerStarted = new RegExp("Listening on"); if (regexServerStarted.test(data)) { logger.verbose(data); resolve(tryCpServer); diff --git a/ts/src/types.ts b/ts/src/types.ts index 4494ddbf..b7b86444 100644 --- a/ts/src/types.ts +++ b/ts/src/types.ts @@ -1,7 +1,6 @@ import { AdminWebsocket, AgentPubKey, - AppBundleSource, AppSignalCb, AppWebsocket, CallZomeRequest, @@ -14,6 +13,7 @@ import { MembraneProof, RoleId, RegisterDnaRequest, + InstalledAppId, } from "@holochain/client"; /** @@ -94,6 +94,65 @@ export interface HappBundleOptions { membraneProofs?: Record; } +/** + * DNA source and additional options. + * + * @public + */ +export interface Dna { + source: DnaSource; + membraneProof?: MembraneProof; + properties?: DnaProperties; + roleId?: string; +} + +/** + * DNAs per agent. Optionally an agent pub key. + * + * @public + */ +export interface AgentDnas { + dnas: Dna[]; + agentPubKey?: AgentPubKey; +} + +/** + * An array of DNA sources for each agent (2-dimensional array) or an array of DNAs + * and an optional agent pub key. Optionally a UID to be used for DNA installation. + * + * @public + */ +export type AgentsHappsOptions = + | DnaSource[][] + | { + agentsDnas: AgentDnas[]; + + /** + * A unique ID for the DNAs (optional). + */ + uid?: string; + + /** + * A unique ID for the hApp (optional). + */ + installedAppId?: InstalledAppId; + }; + +/** + * Player installation options used in scenarios. + * + * Specifies either only the DNA sources that the hApp to be installed + * consists of, or the DNAs and a signal handler to be registered. + * + * @public + */ +export type PlayerHappOptions = + | DnaSource[] + | { + dnas: Dna[]; + signalHandler?: AppSignalCb; + }; + /** * Base interface of a Tryorama conductor. Both {@link Conductor} and * {@link TryCpConductor} implement this interface. @@ -117,49 +176,5 @@ export interface IConductor { >; appWs: () => Pick; - installAgentsHapps: (options: { - agentsDnas: DnaSource[][]; - uid?: string; - properties?: DnaProperties; - signalHandler?: AppSignalCb; - }) => Promise; -} - -/** - * A type that specifies either only the DNAs that the hApp to be installed - * consists of, or the DNAs and a signal handler to be registered. - * - * @public - */ -export type AgentHappOptions = - | DnaSource[] - | { - dnas: DnaSource[]; - signalHandler?: AppSignalCb; - properties?: DnaProperties; - }; - -/** - * Base interface of a Tryorama test scenario. Both {@link Scenario} and - * {@link TryCpScenario} implement this interface. - * - * @public - */ -export interface IScenario { - addConductor(signalHandler?: AppSignalCb): Promise; - addPlayerWithHapp(agentHappOptions: AgentHappOptions): Promise; - addPlayersWithHapps(agentHappOptions: AgentHappOptions[]): Promise; - addPlayerWithHappBundle( - appBundleSource: AppBundleSource, - options?: HappBundleOptions & { signalHandler?: AppSignalCb } - ): Promise; - addPlayersWithHappBundles( - playersHappBundles: Array<{ - appBundleSource: AppBundleSource; - options?: HappBundleOptions & { signalHandler?: AppSignalCb }; - }> - ): Promise; - shareAllAgents(conductors: IConductor[]): Promise; - shutDown(): Promise; - cleanUp(): Promise; + installAgentsHapps: (options: AgentsHappsOptions) => Promise; } diff --git a/ts/test/fixture/dna.yaml b/ts/test/fixture/dna.yaml index ac6af71f..0c7a6751 100644 --- a/ts/test/fixture/dna.yaml +++ b/ts/test/fixture/dna.yaml @@ -5,5 +5,9 @@ integrity: uid: 9a28aac8-337c-11eb-adc1-0Z02acw20115 properties: ~ zomes: - - name: crud - bundled: "../../../target/wasm32-unknown-unknown/release/crud.wasm" + - name: integrity + bundled: "../../../target/wasm32-unknown-unknown/release/integrity.wasm" +coordinator: + zomes: + - name: coordinator + bundled: "../../../target/wasm32-unknown-unknown/release/coordinator.wasm" diff --git a/ts/test/fixture/zomes/entry/Cargo.toml b/ts/test/fixture/zomes/coordinator/Cargo.toml similarity index 56% rename from ts/test/fixture/zomes/entry/Cargo.toml rename to ts/test/fixture/zomes/coordinator/Cargo.toml index 39fa61bf..31cce80c 100644 --- a/ts/test/fixture/zomes/entry/Cargo.toml +++ b/ts/test/fixture/zomes/coordinator/Cargo.toml @@ -1,12 +1,13 @@ [package] -name = "crud" edition = "2021" +name = "coordinator" version = "0.1.0" [lib] crate-type = ["cdylib", "rlib"] -name = "crud" +name = "coordinator" [dependencies] hdk = "0.0.140" -serde = "1" \ No newline at end of file +integrity = {path = "../integrity"} +serde = "1" diff --git a/ts/test/fixture/zomes/entry/src/lib.rs b/ts/test/fixture/zomes/coordinator/src/lib.rs similarity index 82% rename from ts/test/fixture/zomes/entry/src/lib.rs rename to ts/test/fixture/zomes/coordinator/src/lib.rs index d513046c..7b1e5082 100644 --- a/ts/test/fixture/zomes/entry/src/lib.rs +++ b/ts/test/fixture/zomes/coordinator/src/lib.rs @@ -1,19 +1,5 @@ use hdk::prelude::*; - -#[hdk_entry_helper] -pub struct Content(String); - -#[hdk_entry_helper] -pub struct UpdateInput { - pub hash: ActionHash, - pub content: String, -} - -#[hdk_entry_defs] -#[unit_enum(EntryTypesUnit)] -pub enum EntryTypes { - Content(Content), -} +use integrity::{Content, EntryTypes, UpdateInput}; #[hdk_extern] pub fn create(input: Content) -> ExternResult { diff --git a/ts/test/fixture/zomes/integrity/Cargo.toml b/ts/test/fixture/zomes/integrity/Cargo.toml new file mode 100644 index 00000000..bd7302bf --- /dev/null +++ b/ts/test/fixture/zomes/integrity/Cargo.toml @@ -0,0 +1,12 @@ +[package] +edition = "2021" +name = "integrity" +version = "0.1.0" + +[lib] +crate-type = ["cdylib", "rlib"] +name = "integrity" + +[dependencies] +holochain_deterministic_integrity = "0.0.12" +serde = "1" diff --git a/ts/test/fixture/zomes/integrity/src/lib.rs b/ts/test/fixture/zomes/integrity/src/lib.rs new file mode 100644 index 00000000..89f8f98b --- /dev/null +++ b/ts/test/fixture/zomes/integrity/src/lib.rs @@ -0,0 +1,16 @@ +use holochain_deterministic_integrity::prelude::*; + +#[hdk_entry_helper] +pub struct Content(pub String); + +#[hdk_entry_helper] +pub struct UpdateInput { + pub hash: ActionHash, + pub content: String, +} + +#[hdk_entry_defs] +#[unit_enum(EntryTypesUnit)] +pub enum EntryTypes { + Content(Content), +} diff --git a/ts/test/local/conductor.ts b/ts/test/local/conductor.ts index 94f86abd..c83c53d9 100644 --- a/ts/test/local/conductor.ts +++ b/ts/test/local/conductor.ts @@ -166,9 +166,9 @@ test("Local Conductor - Spawn a conductor and check for admin and app ws", async test("Local Conductor - Get app info", async (t) => { const conductor = await createConductor(); - const [aliceHapps] = await conductor.installAgentsHapps({ - agentsDnas: [[{ path: FIXTURE_DNA_URL.pathname }]], - }); + const [aliceHapps] = await conductor.installAgentsHapps([ + [{ path: FIXTURE_DNA_URL.pathname }], + ]); const appInfo = await conductor.appWs().appInfo({ installed_app_id: aliceHapps.happId, }); @@ -187,14 +187,14 @@ test("Local Conductor - Install and call a hApp bundle", async (t) => { const entryContent = "Bye bye, world"; const createEntryResponse: EntryHash = await installedHappBundle.cells[0].callZome({ - zome_name: "crud", + zome_name: "coordinator", fn_name: "create", payload: entryContent, }); t.ok(createEntryResponse); const readEntryResponse: typeof entryContent = await installedHappBundle.cells[0].callZome({ - zome_name: "crud", + zome_name: "coordinator", fn_name: "read", payload: createEntryResponse, }); @@ -207,12 +207,16 @@ test("Local Conductor - Install and call a hApp bundle", async (t) => { test("Local Conductor - Get a convenience function for zome calls", async (t) => { const conductor = await createConductor(); const [aliceHapps] = await conductor.installAgentsHapps({ - agentsDnas: [[{ path: FIXTURE_DNA_URL.pathname }]], + agentsDnas: [{ dnas: [{ source: { path: FIXTURE_DNA_URL.pathname } }] }], }); - const crudZomeCall = getZomeCaller(aliceHapps.cells[0], "crud"); - t.equal(typeof crudZomeCall, "function", "getZomeCaller returns a function"); + const coordinatorZomeCall = getZomeCaller(aliceHapps.cells[0], "coordinator"); + t.equal( + typeof coordinatorZomeCall, + "function", + "getZomeCaller returns a function" + ); - const entryHeaderHash: ActionHash = await crudZomeCall( + const entryHeaderHash: ActionHash = await coordinatorZomeCall( "create", "test-entry" ); @@ -226,12 +230,10 @@ test("Local Conductor - Get a convenience function for zome calls", async (t) => test("Local Conductor - Install multiple agents and DNAs and get access to agents and cells", async (t) => { const conductor = await createConductor(); - const [aliceHapps, bobHapps] = await conductor.installAgentsHapps({ - agentsDnas: [ - [{ path: FIXTURE_DNA_URL.pathname }, { path: FIXTURE_DNA_URL.pathname }], - [{ path: FIXTURE_DNA_URL.pathname }, { path: FIXTURE_DNA_URL.pathname }], - ], - }); + const [aliceHapps, bobHapps] = await conductor.installAgentsHapps([ + [{ path: FIXTURE_DNA_URL.pathname }, { path: FIXTURE_DNA_URL.pathname }], + [{ path: FIXTURE_DNA_URL.pathname }, { path: FIXTURE_DNA_URL.pathname }], + ]); aliceHapps.cells.forEach((cell) => t.deepEqual(cell.cell_id[1], aliceHapps.agentPubKey) ); @@ -243,6 +245,28 @@ test("Local Conductor - Install multiple agents and DNAs and get access to agent await cleanAllConductors(); }); +test("Local Conductor - Install a DNA with custom role id", async (t) => { + const conductor = await createConductor(); + const expectedRoleId = "test-role-id"; + const [aliceHapps] = await conductor.installAgentsHapps({ + agentsDnas: [ + { + dnas: [ + { + source: { path: FIXTURE_DNA_URL.pathname }, + roleId: expectedRoleId, + }, + ], + }, + ], + }); + const actualRoleId = aliceHapps.cells[0].role_id; + t.equal(actualRoleId, expectedRoleId, "dna role id matches"); + + await conductor.shutDown(); + await cleanAllConductors(); +}); + test("Local Conductor - Install hApp bundle and access cells with role ids", async (t) => { const conductor = await createConductor(); const aliceHapp = await conductor.installHappBundle({ @@ -262,7 +286,9 @@ test("Local Conductor - Zome call can time out before completion", async (t) => const cell = aliceHapp.namedCells.get("test"); assert(cell); - await t.rejects(cell.callZome({ fn_name: "create", zome_name: "crud" }, 1)); + await t.rejects( + cell.callZome({ fn_name: "create", zome_name: "coordinator" }, 1) + ); await conductor.shutDown(); await cleanAllConductors(); @@ -298,7 +324,7 @@ test("Local Conductor - Create and read an entry using the entry zome", async (t const createEntryHash: EntryHash = await conductor.appWs().callZome({ cap_secret: null, cell_id, - zome_name: "crud", + zome_name: "coordinator", fn_name: "create", provenance: agentPubKey, payload: entryContent, @@ -312,7 +338,7 @@ test("Local Conductor - Create and read an entry using the entry zome", async (t .callZome({ cap_secret: null, cell_id, - zome_name: "crud", + zome_name: "coordinator", fn_name: "read", provenance: agentPubKey, payload: createEntryHash, @@ -328,15 +354,16 @@ test("Local Conductor - Create and read an entry using the entry zome, 2 conduct const conductor1 = await createConductor(); const conductor2 = await createConductor(); - const [aliceHapps, bobHapps] = await conductor1.installAgentsHapps({ - agentsDnas: [dnas, dnas], - }); + const [aliceHapps, bobHapps] = await conductor1.installAgentsHapps([ + dnas, + dnas, + ]); await addAllAgentsToAllConductors([conductor1, conductor2]); const entryContent = "test-content"; const createEntryHash: EntryHash = await aliceHapps.cells[0].callZome({ - zome_name: "crud", + zome_name: "coordinator", fn_name: "create", payload: entryContent, }); @@ -348,7 +375,7 @@ test("Local Conductor - Create and read an entry using the entry zome, 2 conduct const readEntryResponse: typeof entryContent = await bobHapps.cells[0].callZome({ - zome_name: "crud", + zome_name: "coordinator", fn_name: "read", payload: createEntryHash, }); @@ -370,13 +397,11 @@ test("Local Conductor - Receive a signal", async (t) => { }); const conductor = await createConductor({ signalHandler, timeout: 30000 }); - const [aliceHapps] = await conductor.installAgentsHapps({ - agentsDnas: [dnas], - }); + const [aliceHapps] = await conductor.installAgentsHapps([dnas]); const aliceSignal = { value: "signal" }; aliceHapps.cells[0].callZome({ - zome_name: "crud", + zome_name: "coordinator", fn_name: "signal_loopback", payload: aliceSignal, }); diff --git a/ts/test/local/scenario.ts b/ts/test/local/scenario.ts index acd8dbfe..ec95f34d 100644 --- a/ts/test/local/scenario.ts +++ b/ts/test/local/scenario.ts @@ -6,6 +6,7 @@ import { } from "@holochain/client"; import test from "tape-promise/tape.js"; import { runScenario, Scenario } from "../../src/local/scenario.js"; +import { Dna } from "../../src/types.js"; import { pause } from "../../src/util.js"; import { FIXTURE_DNA_URL, FIXTURE_HAPP_URL } from "../fixture/index.js"; @@ -20,8 +21,8 @@ test("Local Scenario - runScenario - Install hApp bundle and access cells throug test("Local Scenario - runScenario - Catch error when calling non-existent zome", async (t) => { await runScenario(async (scenario: Scenario) => { - const alice = await scenario.addPlayerWithHapp([ - { path: FIXTURE_DNA_URL.pathname }, + const [alice] = await scenario.addPlayersWithHapps([ + [{ path: FIXTURE_DNA_URL.pathname }], ]); await t.rejects( @@ -35,8 +36,8 @@ test("Local Scenario - runScenario - Catch error when calling non-existent zome" test("Local Scenario - runScenario - Catch error when attaching a protected port", async (t) => { await runScenario(async (scenario: Scenario) => { - const alice = await scenario.addPlayerWithHapp([ - { path: FIXTURE_DNA_URL.pathname }, + const [alice] = await scenario.addPlayersWithHapps([ + [{ path: FIXTURE_DNA_URL.pathname }], ]); await t.rejects(alice.conductor.attachAppInterface({ port: 300 })); @@ -45,8 +46,8 @@ test("Local Scenario - runScenario - Catch error when attaching a protected port test("Local Scenario - runScenario - Catch error when calling a zome of an undefined cell", async (t) => { await runScenario(async (scenario: Scenario) => { - const alice = await scenario.addPlayerWithHapp([ - { path: FIXTURE_DNA_URL.pathname }, + const [alice] = await scenario.addPlayersWithHapps([ + [{ path: FIXTURE_DNA_URL.pathname }], ]); t.throws(() => alice.cells[2].callZome({ zome_name: "", fn_name: "" })); @@ -64,14 +65,14 @@ test("Local Scenario - runScenario - Catch error that occurs in a signal handler const [alice] = await scenario.addPlayersWithHapps([ { - dnas: [{ path: FIXTURE_DNA_URL.pathname }], + dnas: [{ source: { path: FIXTURE_DNA_URL.pathname } }], signalHandler: signalHandlerAlice, }, ]); const signalAlice = { value: "hello alice" }; alice.cells[0].callZome({ - zome_name: "crud", + zome_name: "coordinator", fn_name: "signal_loopback", payload: signalAlice, }); @@ -114,7 +115,7 @@ test("Local Scenario - Create and read an entry, 2 conductors", async (t) => { const content = "Hi dare"; const createEntryHash = await alice.cells[0].callZome({ - zome_name: "crud", + zome_name: "coordinator", fn_name: "create", payload: content, }); @@ -122,7 +123,7 @@ test("Local Scenario - Create and read an entry, 2 conductors", async (t) => { await pause(100); const readContent = await bob.cells[0].callZome({ - zome_name: "crud", + zome_name: "coordinator", fn_name: "read", payload: createEntryHash, }); @@ -142,7 +143,7 @@ test("Local Scenario - Conductor maintains data after shutdown and restart", asy const content = "Before shutdown"; const createEntryHash = await alice.cells[0].callZome({ - zome_name: "crud", + zome_name: "coordinator", fn_name: "create", payload: content, }); @@ -150,7 +151,7 @@ test("Local Scenario - Conductor maintains data after shutdown and restart", asy await pause(100); const readContent = await bob.cells[0].callZome({ - zome_name: "crud", + zome_name: "coordinator", fn_name: "read", payload: createEntryHash, }); @@ -162,7 +163,7 @@ test("Local Scenario - Conductor maintains data after shutdown and restart", asy await bob.conductor.startUp(); await bob.conductor.connectAppInterface(); const readContentAfterRestart = await bob.cells[0].callZome({ - zome_name: "crud", + zome_name: "coordinator", fn_name: "read", payload: createEntryHash, }); @@ -173,7 +174,7 @@ test("Local Scenario - Conductor maintains data after shutdown and restart", asy test("Local Scenario - Receive signals with 2 conductors", async (t) => { const scenario = new Scenario(); - const dnas: DnaSource[] = [{ path: FIXTURE_DNA_URL.pathname }]; + const dnas: Dna[] = [{ source: { path: FIXTURE_DNA_URL.pathname } }]; let signalHandlerAlice: AppSignalCb | undefined; const signalReceivedAlice = new Promise((resolve) => { @@ -196,13 +197,13 @@ test("Local Scenario - Receive signals with 2 conductors", async (t) => { const signalAlice = { value: "hello alice" }; alice.cells[0].callZome({ - zome_name: "crud", + zome_name: "coordinator", fn_name: "signal_loopback", payload: signalAlice, }); const signalBob = { value: "hello bob" }; bob.cells[0].callZome({ - zome_name: "crud", + zome_name: "coordinator", fn_name: "signal_loopback", payload: signalBob, }); diff --git a/ts/test/trycp/server.ts b/ts/test/trycp/client.ts similarity index 78% rename from ts/test/trycp/server.ts rename to ts/test/trycp/client.ts index 5e91ff22..b3746060 100644 --- a/ts/test/trycp/server.ts +++ b/ts/test/trycp/client.ts @@ -1,8 +1,7 @@ import { Buffer } from "node:buffer"; -import test from "tape-promise/tape.js"; import { URL } from "node:url"; +import test from "tape-promise/tape.js"; import { - cleanAllTryCpConductors, createTryCpConductor, DEFAULT_PARTIAL_PLAYER_CONFIG, } from "../../src/trycp/index.js"; @@ -30,7 +29,7 @@ test("TryCP Server - Ping", async (t) => { await localTryCpServer.stop(); }); -test("TryCP Server - Non-existent call throws", async (t) => { +test("TryCP Server - non-existent call throws", async (t) => { const localTryCpServer = await TryCpServer.start(); const tryCpClient = await createTryCpClient(); @@ -47,7 +46,7 @@ test("TryCP Server - Non-existent call throws", async (t) => { await localTryCpServer.stop(); }); -test("TryCP Server - Download DNA from web", async (t) => { +test("TryCP Server - download DNA from web", async (t) => { const localTryCpServer = await TryCpServer.start(); const tryCpClient = await createTryCpClient(); @@ -64,7 +63,7 @@ test("TryCP Server - Download DNA from web", async (t) => { await localTryCpServer.stop(); }); -test("TryCP Server - Download DNA from file system", async (t) => { +test("TryCP Server - download DNA from file system", async (t) => { const localTryCpServer = await TryCpServer.start(); const tryCpClient = await createTryCpClient(); @@ -80,7 +79,7 @@ test("TryCP Server - Download DNA from file system", async (t) => { await localTryCpServer.stop(); }); -test("TryCP Server - Save DNA to file system", async (t) => { +test("TryCP Server - save DNA to file system", async (t) => { const localTryCpServer = await TryCpServer.start(); const tryCpClient = await createTryCpClient(); @@ -96,7 +95,7 @@ test("TryCP Server - Save DNA to file system", async (t) => { await localTryCpServer.stop(); }); -test("TryCP Server - Configure player", async (t) => { +test("TryCP Server - configure player", async (t) => { const localTryCpServer = await TryCpServer.start(); const tryCpClient = await createTryCpClient(); @@ -161,7 +160,7 @@ test("TryCP Server - 2 parallel calls from two clients return correct responses" await localTryCpServer.stop(); }); -test("TryCP Server - Startup and shutdown", async (t) => { +test("TryCP Server - startup and shutdown", async (t) => { const localTryCpServer = await TryCpServer.start(); const tryCpClient = await createTryCpClient(); @@ -187,7 +186,7 @@ test("TryCP Server - Startup and shutdown", async (t) => { await localTryCpServer.stop(); }); -test("TryCP Server - Reset", async (t) => { +test("TryCP Server - reset", async (t) => { const localTryCpServer = await TryCpServer.start(); const tryCpClient = await createTryCpClient(); @@ -216,16 +215,46 @@ test("TryCP Server - Reset", async (t) => { await localTryCpServer.stop(); }); -test("TryCP Server - Clean all conductors", async (t) => { +test("TryCP Server - clean all conductors", async (t) => { const localTryCpServer = await TryCpServer.start(); - const actual = await cleanAllTryCpConductors(SERVER_URL); + const tryCpClient = await createTryCpClient(); + const actual = await tryCpClient.cleanAllConductors(); t.equal(actual, TRYCP_SUCCESS_RESPONSE); + await tryCpClient.close(); await localTryCpServer.stop(); }); -test("TryCP Server - Admin API - Connect app interface", async (t) => { +test("TryCP Client - shut down", async (t) => { const localTryCpServer = await TryCpServer.start(); - const conductor = await createTryCpConductor(SERVER_URL); + const client = await createTryCpClient(); + const conductor1 = await client.addConductor(); + const conductor2 = await client.addConductor(); + const conductor3 = await client.addConductor(); + t.doesNotReject( + conductor1.adminWs().generateAgentPubKey, + "conductor 1 responds" + ); + t.doesNotReject( + conductor2.adminWs().generateAgentPubKey, + "conductor 2 responds" + ); + t.doesNotReject( + conductor3.adminWs().generateAgentPubKey, + "conductor 3 responds" + ); + await client.shutDownConductors(); + t.rejects(conductor1.adminWs().generateAgentPubKey, "conductor 1 is down"); + t.rejects(conductor2.adminWs().generateAgentPubKey, "conductor 2 is down"); + t.rejects(conductor3.adminWs().generateAgentPubKey, "conductor 3 is down"); + + await client.cleanUp(); + await localTryCpServer.stop(); +}); + +test("TryCP Server - Admin API - connect app interface", async (t) => { + const localTryCpServer = await TryCpServer.start(); + const tryCpClient = await createTryCpClient(); + const conductor = await createTryCpConductor(tryCpClient); const { port } = await conductor.adminWs().attachAppInterface(); t.ok(typeof port === "number"); @@ -247,12 +276,13 @@ test("TryCP Server - Admin API - Connect app interface", async (t) => { await localTryCpServer.stop(); }); -test("TryCP Server - App API - Get app info", async (t) => { +test("TryCP Server - App API - get app info", async (t) => { const localTryCpServer = await TryCpServer.start(); - const conductor = await createTryCpConductor(SERVER_URL); - const [alice] = await conductor.installAgentsHapps({ - agentsDnas: [[{ path: FIXTURE_DNA_URL.pathname }]], - }); + const tryCpClient = await createTryCpClient(); + const conductor = await createTryCpConductor(tryCpClient); + const [alice] = await conductor.installAgentsHapps([ + [{ path: FIXTURE_DNA_URL.pathname }], + ]); await conductor.adminWs().attachAppInterface(); await conductor.connectAppInterface(); diff --git a/ts/test/trycp/conductor.ts b/ts/test/trycp/conductor.ts index 553ccc1c..784cd314 100644 --- a/ts/test/trycp/conductor.ts +++ b/ts/test/trycp/conductor.ts @@ -1,10 +1,10 @@ import { AppSignal, DnaSource, EntryHash } from "@holochain/client"; import { Buffer } from "node:buffer"; -import test from "tape-promise/tape.js"; import { URL } from "node:url"; +import test from "tape-promise/tape.js"; import { addAllAgentsToAllConductors } from "../../src/common.js"; +import { Dna, TryCpClient } from "../../src/index.js"; import { - cleanAllTryCpConductors, createTryCpConductor, TryCpConductorOptions, } from "../../src/trycp/conductor/index.js"; @@ -27,53 +27,81 @@ network: - type: quic network_type: quic_mdns`; -const createTestTryCpConductor = (options?: TryCpConductorOptions) => - createTryCpConductor(SERVER_URL, { +const createTestConductor = ( + client: TryCpClient, + options?: TryCpConductorOptions +) => + createTryCpConductor(client, { partialConfig: LOCAL_TEST_PARTIAL_CONFIG, ...options, }); -test("TryCP Conductor - Stop and restart a conductor", async (t) => { +test("TryCP Conductor - stop and restart a conductor", async (t) => { const localTryCpServer = await TryCpServer.start(); - const conductor = await createTestTryCpConductor({ timeout: 30000 }); + const client = await TryCpClient.create(SERVER_URL, 60000); + const conductor = await createTestConductor(client); const agentPubKeyResponse = await conductor.adminWs().generateAgentPubKey(); - t.ok(agentPubKeyResponse); + t.ok(agentPubKeyResponse, "agent pub key generated before shutdown"); await conductor.shutDown(); - await t.rejects(conductor.adminWs().generateAgentPubKey); + await t.rejects( + conductor.adminWs().generateAgentPubKey, + "conductor rejects request after shutdown" + ); await conductor.startUp({}); const agentPubKeyResponse2 = await conductor.adminWs().generateAgentPubKey(); - t.ok(agentPubKeyResponse2); + t.ok(agentPubKeyResponse2, "agent pub key generated after restart"); - await conductor.shutDown(); - await conductor.disconnectClient(); - await cleanAllTryCpConductors(SERVER_URL); + await client.cleanUp(); await localTryCpServer.stop(); }); -test("TryCP Conductor - Install hApp bundle and access cells with role ids", async (t) => { +test("TryCP Conductor - provide agent pub keys when installing hApp", async (t) => { const localTryCpServer = await TryCpServer.start(); - const conductor = await createTestTryCpConductor(); + const client = await TryCpClient.create(SERVER_URL, 60000); + const conductor = await createTestConductor(client); + + const agentPubKey = await conductor.adminWs().generateAgentPubKey(); + t.ok(agentPubKey, "agent pub key generated"); + + const [alice] = await conductor.installAgentsHapps({ + agentsDnas: [ + { dnas: [{ source: { path: FIXTURE_DNA_URL.pathname } }], agentPubKey }, + ], + }); + t.deepEqual( + alice.agentPubKey, + agentPubKey, + "alice's agent pub key matches provided key" + ); + + await client.cleanUp(); + await localTryCpServer.stop(); +}); + +test("TryCP Conductor - install hApp bundle and access cells with role ids", async (t) => { + const localTryCpServer = await TryCpServer.start(); + const client = await TryCpClient.create(SERVER_URL, 60000); + const conductor = await createTestConductor(client); const aliceHapp = await conductor.installHappBundle({ path: FIXTURE_HAPP_URL.pathname, }); - t.ok(aliceHapp.namedCells.get("test")); + t.ok(aliceHapp.namedCells.get("test"), "named cell can be accessed"); - await conductor.shutDown(); - await conductor.disconnectClient(); - await cleanAllTryCpConductors(SERVER_URL); + await client.cleanUp(); await localTryCpServer.stop(); }); -test("TryCP Conductor - Install and call a hApp bundle", async (t) => { +test("TryCP Conductor - install and call a hApp bundle", async (t) => { const localTryCpServer = await TryCpServer.start(); - const conductor = await createTestTryCpConductor(); + const client = await TryCpClient.create(SERVER_URL, 60000); + const conductor = await createTestConductor(client); const installedHappBundle = await conductor.installHappBundle({ path: FIXTURE_HAPP_URL.pathname, }); - t.ok(installedHappBundle.happId); + t.ok(installedHappBundle.happId, "installed hApp bundle has a hApp id"); await conductor.adminWs().attachAppInterface(); await conductor.connectAppInterface(); @@ -81,27 +109,32 @@ test("TryCP Conductor - Install and call a hApp bundle", async (t) => { const entryContent = "Bye bye, world"; const createEntryResponse: EntryHash = await installedHappBundle.cells[0].callZome({ - zome_name: "crud", + zome_name: "coordinator", fn_name: "create", payload: entryContent, }); - t.ok(createEntryResponse); + t.ok(createEntryResponse, "entry created successfully"); const readEntryResponse: typeof entryContent = await installedHappBundle.cells[0].callZome({ - zome_name: "crud", + zome_name: "coordinator", fn_name: "read", payload: createEntryResponse, }); - t.equal(readEntryResponse, entryContent); + t.equal( + readEntryResponse, + entryContent, + "read entry content matches created entry content" + ); - await conductor.shutDown(); - await conductor.disconnectClient(); - await cleanAllTryCpConductors(SERVER_URL); + await client.cleanUp(); await localTryCpServer.stop(); }); -test("TryCP Conductor - Receive a signal", async (t) => { +test("TryCP Conductor - receive a signal", async (t) => { const localTryCpServer = await TryCpServer.start(); + const client = await TryCpClient.create(SERVER_URL, 30000); + const conductor = await createTestConductor(client); + const testSignal = { value: "signal" }; let signalHandler; @@ -111,7 +144,6 @@ test("TryCP Conductor - Receive a signal", async (t) => { }; }); - const conductor = await createTestTryCpConductor({ timeout: 30000 }); const agentPubKey = await conductor.adminWs().generateAgentPubKey(); const installedAppBundle = await conductor.adminWs().installAppBundle({ agent_key: agentPubKey, @@ -127,34 +159,37 @@ test("TryCP Conductor - Receive a signal", async (t) => { conductor.appWs().callZome({ cap_secret: null, cell_id: installedAppBundle.cell_data[0].cell_id, - zome_name: "crud", + zome_name: "coordinator", fn_name: "signal_loopback", provenance: agentPubKey, payload: testSignal, }); const actualSignal = await signalReceived; - t.deepEqual(actualSignal.data.payload, testSignal); + t.deepEqual( + actualSignal.data.payload, + testSignal, + "received signal matches expected signal" + ); - await conductor.shutDown(); - await conductor.disconnectClient(); - await cleanAllTryCpConductors(SERVER_URL); + await client.cleanUp(); await localTryCpServer.stop(); }); -test("TryCP Conductor - Create and read an entry using the entry zome", async (t) => { +test("TryCP Conductor - create and read an entry using the entry zome", async (t) => { const localTryCpServer = await TryCpServer.start(); - const conductor = await createTestTryCpConductor(); + const client = await TryCpClient.create(SERVER_URL, 60000); + const conductor = await createTestConductor(client); const relativePath = await conductor.downloadDna(FIXTURE_DNA_URL); const dnaHash = await conductor.adminWs().registerDna({ path: relativePath }); const dnaHashB64 = Buffer.from(dnaHash).toString("base64"); - t.equal(dnaHash.length, 39); - t.ok(dnaHashB64.startsWith("hC0k")); + t.equal(dnaHash.length, 39, "DNA hash is 39 bytes long"); + t.ok(dnaHashB64.startsWith("hC0k"), "DNA hash starts with hC0k"); const agentPubKey = await conductor.adminWs().generateAgentPubKey(); const agentPubKeyB64 = Buffer.from(agentPubKey).toString("base64"); - t.equal(agentPubKey.length, 39); - t.ok(agentPubKeyB64.startsWith("hCAk")); + t.equal(agentPubKey.length, 39, "agent pub key is 39 bytes long"); + t.ok(agentPubKeyB64.startsWith("hCAk"), "agent pub key starts with hCAk"); const appId = "entry-app"; const installedAppInfo = await conductor.adminWs().installApp({ @@ -163,81 +198,102 @@ test("TryCP Conductor - Create and read an entry using the entry zome", async (t dnas: [{ hash: dnaHash, role_id: "entry-dna" }], }); const { cell_id } = installedAppInfo.cell_data[0]; - t.ok(Buffer.from(cell_id[0]).toString("base64").startsWith("hC0k")); - t.ok(Buffer.from(cell_id[1]).toString("base64").startsWith("hCAk")); + t.ok( + Buffer.from(cell_id[0]).toString("base64").startsWith("hC0k"), + "first part of cell id start with hC0k" + ); + t.ok( + Buffer.from(cell_id[1]).toString("base64").startsWith("hCAk"), + "second part of cell id starts with hC0k" + ); const enabledAppResponse = await conductor.adminWs().enableApp({ installed_app_id: appId, }); - t.deepEqual(enabledAppResponse.app.status, { running: null }); + t.deepEqual( + enabledAppResponse.app.status, + { running: null }, + "enabled app response matches 'running'" + ); await conductor.adminWs().attachAppInterface(); const connectAppInterfaceResponse = await conductor.connectAppInterface(); - t.equal(connectAppInterfaceResponse, TRYCP_SUCCESS_RESPONSE); + t.equal( + connectAppInterfaceResponse, + TRYCP_SUCCESS_RESPONSE, + "connected app interface responds with success" + ); const entryContent = "test-content"; const createEntryHash = await conductor.appWs().callZome({ cap_secret: null, cell_id, - zome_name: "crud", + zome_name: "coordinator", fn_name: "create", provenance: agentPubKey, payload: entryContent, }); const createdEntryHashB64 = Buffer.from(createEntryHash).toString("base64"); - t.equal(createEntryHash.length, 39); - t.ok(createdEntryHashB64.startsWith("hCkk")); + t.equal(createEntryHash.length, 39, "created entry hash is 39 bytes long"); + t.ok( + createdEntryHashB64.startsWith("hCkk"), + "created entry hash starts with hCkk" + ); const readEntryResponse = await conductor .appWs() .callZome({ cap_secret: null, cell_id, - zome_name: "crud", + zome_name: "coordinator", fn_name: "read", provenance: agentPubKey, payload: createEntryHash, }); - t.equal(readEntryResponse, entryContent); + t.equal( + readEntryResponse, + entryContent, + "read entry content matches created entry content" + ); const disconnectAppInterfaceResponse = await conductor.disconnectAppInterface(); - t.equal(disconnectAppInterfaceResponse, TRYCP_SUCCESS_RESPONSE); + t.equal( + disconnectAppInterfaceResponse, + TRYCP_SUCCESS_RESPONSE, + "disconnect app interface responds with success" + ); - await conductor.shutDown(); - await conductor.disconnectClient(); - await cleanAllTryCpConductors(SERVER_URL); + await client.cleanUp(); await localTryCpServer.stop(); }); -test("TryCP Conductor - Reading a non-existent entry returns null", async (t) => { +test("TryCP Conductor - reading a non-existent entry returns null", async (t) => { const localTryCpServer = await TryCpServer.start(); - const conductor = await createTestTryCpConductor(); + const client = await TryCpClient.create(SERVER_URL, 60000); + const conductor = await createTestConductor(client); const dnas = [{ path: FIXTURE_DNA_URL.pathname }]; - const [alice_happs] = await conductor.installAgentsHapps({ - agentsDnas: [dnas], - }); + const [alice_happs] = await conductor.installAgentsHapps([dnas]); await conductor.adminWs().attachAppInterface(); await conductor.connectAppInterface(); const actual = await alice_happs.cells[0].callZome({ - zome_name: "crud", + zome_name: "coordinator", fn_name: "read", provenance: alice_happs.agentPubKey, payload: Buffer.from("hCkk", "base64"), }); - t.equal(actual, null); + t.equal(actual, null, "read a non-existing entry returns null"); - await conductor.shutDown(); - await conductor.disconnectClient(); - await cleanAllTryCpConductors(SERVER_URL); + await client.cleanUp(); await localTryCpServer.stop(); }); -test("TryCP Conductor - Create and read an entry using the entry zome, 1 conductor, 2 cells, 2 agents", async (t) => { +test("TryCP Conductor - create and read an entry using the entry zome, 1 conductor, 2 cells, 2 agents", async (t) => { const localTryCpServer = await TryCpServer.start(); - const conductor = await createTestTryCpConductor(); + const client = await TryCpClient.create(SERVER_URL, 60000); + const conductor = await createTestConductor(client); const relativePath = await conductor.downloadDna(FIXTURE_DNA_URL); const dnaHash1 = await conductor @@ -248,20 +304,20 @@ test("TryCP Conductor - Create and read an entry using the entry zome, 1 conduct .registerDna({ path: relativePath }); const dnaHash1B64 = Buffer.from(dnaHash1).toString("base64"); const dnaHash2B64 = Buffer.from(dnaHash1).toString("base64"); - t.equal(dnaHash1.length, 39); - t.ok(dnaHash1B64.startsWith("hC0k")); - t.equal(dnaHash2.length, 39); - t.ok(dnaHash2B64.startsWith("hC0k")); + t.equal(dnaHash1.length, 39, "DNA hash 1 is 39 bytes long"); + t.ok(dnaHash1B64.startsWith("hC0k"), "DNA hash 1 starts with hC0k"); + t.equal(dnaHash2.length, 39, "DNA hash 2 is 39 bytes long"); + t.ok(dnaHash2B64.startsWith("hC0k"), "DNA hash 2 starts with hC0k"); const agent1PubKey = await conductor.adminWs().generateAgentPubKey(); const agent1PubKeyB64 = Buffer.from(agent1PubKey).toString("base64"); - t.equal(agent1PubKey.length, 39); - t.ok(agent1PubKeyB64.startsWith("hCAk")); + t.equal(agent1PubKey.length, 39), "agent pub key 1 is 39 bytes long"; + t.ok(agent1PubKeyB64.startsWith("hCAk"), "agent pub key 1 starts with hCAk"); const agent2PubKey = await conductor.adminWs().generateAgentPubKey(); const agent2PubKeyB64 = Buffer.from(agent1PubKey).toString("base64"); - t.equal(agent2PubKey.length, 39); - t.ok(agent2PubKeyB64.startsWith("hCAk")); + t.equal(agent2PubKey.length, 39, "agent pub key 2 is 39 bytes long"); + t.ok(agent2PubKeyB64.startsWith("hCAk"), "agent pub key 2 starts with hCAk"); const appId1 = "entry-app1"; const installedAppInfo1 = await conductor.adminWs().installApp({ @@ -270,8 +326,14 @@ test("TryCP Conductor - Create and read an entry using the entry zome, 1 conduct dnas: [{ hash: dnaHash1, role_id: "entry-dna" }], }); const cellId1 = installedAppInfo1.cell_data[0].cell_id; - t.ok(Buffer.from(cellId1[0]).toString("base64").startsWith("hC0k")); - t.ok(Buffer.from(cellId1[1]).toString("base64").startsWith("hCAk")); + t.ok( + Buffer.from(cellId1[0]).toString("base64").startsWith("hC0k"), + "first part of cell id 1 starts with hC0k" + ); + t.ok( + Buffer.from(cellId1[1]).toString("base64").startsWith("hCAk"), + "second part of cell id 1 starts with hCAk" + ); const appId2 = "entry-app2"; const installedAppInfo2 = await conductor.adminWs().installApp({ @@ -280,83 +342,114 @@ test("TryCP Conductor - Create and read an entry using the entry zome, 1 conduct dnas: [{ hash: dnaHash2, role_id: "entry-dna" }], }); const cellId2 = installedAppInfo2.cell_data[0].cell_id; - t.ok(Buffer.from(cellId2[0]).toString("base64").startsWith("hC0k")); - t.ok(Buffer.from(cellId2[1]).toString("base64").startsWith("hCAk")); - t.deepEqual(cellId1[0], cellId2[0]); + t.ok( + Buffer.from(cellId2[0]).toString("base64").startsWith("hC0k"), + "first part of cell id 2 starts with hC0k" + ); + t.ok( + Buffer.from(cellId2[1]).toString("base64").startsWith("hCAk"), + "second part of cell id 2 starts with hCAk" + ); + t.deepEqual( + cellId1[0], + cellId2[0], + "DNA hash of cell 1 matches DNA has of cell 2" + ); const enabledAppResponse1 = await conductor.adminWs().enableApp({ installed_app_id: appId1, }); - t.deepEqual(enabledAppResponse1.app.status, { running: null }); + t.deepEqual( + enabledAppResponse1.app.status, + { running: null }, + "enabled app response 1 matches 'running'" + ); const enabledAppResponse2 = await conductor.adminWs().enableApp({ installed_app_id: appId2, }); - t.deepEqual(enabledAppResponse2.app.status, { running: null }); + t.deepEqual( + enabledAppResponse2.app.status, + { running: null }, + "enabled app response 2 matches 'running'" + ); await conductor.adminWs().attachAppInterface(); const connectAppInterfaceResponse = await conductor.connectAppInterface(); - t.equal(connectAppInterfaceResponse, TRYCP_SUCCESS_RESPONSE); + t.equal( + connectAppInterfaceResponse, + TRYCP_SUCCESS_RESPONSE, + "connect app interface responds with success" + ); const entryContent = "test-content"; const createEntryHash = await conductor.appWs().callZome({ cap_secret: null, cell_id: cellId1, - zome_name: "crud", + zome_name: "coordinator", fn_name: "create", provenance: agent1PubKey, payload: entryContent, }); const createdEntryHashB64 = Buffer.from(createEntryHash).toString("base64"); - t.equal(createEntryHash.length, 39); - t.ok(createdEntryHashB64.startsWith("hCkk")); + t.equal(createEntryHash.length, 39, "created entry hash is 39 bytes long"); + t.ok( + createdEntryHashB64.startsWith("hCkk"), + "created entry hash starts with hCkk" + ); const readEntryResponse = await conductor .appWs() .callZome({ cap_secret: null, cell_id: cellId2, - zome_name: "crud", + zome_name: "coordinator", fn_name: "read", provenance: agent2PubKey, payload: createEntryHash, }); - t.equal(readEntryResponse, entryContent); + t.equal( + readEntryResponse, + entryContent, + "read entry content matches created entry content" + ); - await conductor.shutDown(); - await conductor.disconnectClient(); - await cleanAllTryCpConductors(SERVER_URL); + await client.cleanUp(); await localTryCpServer.stop(); }); -test("TryCP Conductor - Create and read an entry using the entry zome, 2 conductors, 2 cells, 2 agents", async (t) => { +test("TryCP Conductor - create and read an entry using the entry zome, 2 conductors, 2 cells, 2 agents", async (t) => { const localTryCpServer = await TryCpServer.start(); + const client = await TryCpClient.create(SERVER_URL, 60000); const dnas: DnaSource[] = [{ path: FIXTURE_DNA_URL.pathname }]; - const conductor1 = await createTestTryCpConductor(); - const [alice] = await conductor1.installAgentsHapps({ agentsDnas: [dnas] }); + const conductor1 = await createTestConductor(client); + const [alice] = await conductor1.installAgentsHapps([dnas]); await conductor1.adminWs().attachAppInterface(); await conductor1.connectAppInterface(); - const conductor2 = await createTestTryCpConductor(); - const [bob] = await conductor2.installAgentsHapps({ agentsDnas: [dnas] }); + const conductor2 = await createTestConductor(client); + const [bob] = await conductor2.installAgentsHapps([dnas]); await conductor2.adminWs().attachAppInterface(); await conductor2.connectAppInterface(); await addAllAgentsToAllConductors([conductor1, conductor2]); const entryContent = "test-content"; - const createEntry1Hash = await conductor1.appWs().callZome({ + const createEntryHash = await conductor1.appWs().callZome({ cap_secret: null, cell_id: alice.cells[0].cell_id, - zome_name: "crud", + zome_name: "coordinator", fn_name: "create", provenance: alice.agentPubKey, payload: entryContent, }); - const createdEntry1HashB64 = Buffer.from(createEntry1Hash).toString("base64"); - t.equal(createEntry1Hash.length, 39); - t.ok(createdEntry1HashB64.startsWith("hCkk")); + const createdEntryHashB64 = Buffer.from(createEntryHash).toString("base64"); + t.equal(createEntryHash.length, 39, "create entry hash is 39 bytes long"); + t.ok( + createdEntryHashB64.startsWith("hCkk"), + "create entry hash starts with hCkk" + ); await pause(2000); @@ -365,17 +458,40 @@ test("TryCP Conductor - Create and read an entry using the entry zome, 2 conduct .callZome({ cap_secret: null, cell_id: bob.cells[0].cell_id, - zome_name: "crud", + zome_name: "coordinator", fn_name: "read", provenance: bob.agentPubKey, - payload: createEntry1Hash, + payload: createEntryHash, }); - t.equal(readEntryResponse, entryContent); + t.equal( + readEntryResponse, + entryContent, + "read entry content matches created entry content" + ); - await conductor1.shutDown(); - await conductor1.disconnectClient(); - await conductor2.shutDown(); - await conductor2.disconnectClient(); - await cleanAllTryCpConductors(SERVER_URL); + await client.cleanUp(); + await localTryCpServer.stop(); +}); + +test("TryCP Conductor - pass a custom application id to happ installation", async (t) => { + const localTryCpServer = await TryCpServer.start(); + const client = await TryCpClient.create(SERVER_URL, 60000); + + const dnas: Dna[] = [{ source: { path: FIXTURE_DNA_URL.pathname } }]; + + const conductor1 = await createTestConductor(client); + const expectedInstalledAppId = "test-app-id"; + const [alice] = await conductor1.installAgentsHapps({ + agentsDnas: [{ dnas }], + installedAppId: expectedInstalledAppId, + }); + const actualInstalledAppId = alice.happId; + t.equal( + actualInstalledAppId, + expectedInstalledAppId, + "installed app id matches" + ); + + await client.cleanUp(); await localTryCpServer.stop(); }); diff --git a/ts/test/trycp/index.ts b/ts/test/trycp/index.ts index 8a8bdf9a..aa5d025d 100644 --- a/ts/test/trycp/index.ts +++ b/ts/test/trycp/index.ts @@ -1,3 +1,3 @@ export * from "./conductor.js"; -export * from "./server.js"; +export * from "./client.js"; export * from "./scenario.js"; diff --git a/ts/test/trycp/scenario.ts b/ts/test/trycp/scenario.ts index 9a3db37d..ed657f4b 100644 --- a/ts/test/trycp/scenario.ts +++ b/ts/test/trycp/scenario.ts @@ -1,13 +1,10 @@ -import { - AppSignal, - AppSignalCb, - DnaSource, - EntryHash, -} from "@holochain/client"; +import { AppSignal, AppSignalCb, EntryHash } from "@holochain/client"; import test from "tape-promise/tape.js"; import { URL } from "node:url"; +import { Dna } from "../../src/types.js"; import { TryCpScenario } from "../../src/trycp/conductor/scenario.js"; import { + TryCpServer, TRYCP_SERVER_HOST, TRYCP_SERVER_PORT, } from "../../src/trycp/trycp-server.js"; @@ -16,31 +13,122 @@ import { FIXTURE_DNA_URL } from "../fixture/index.js"; const SERVER_URL = new URL(`ws://${TRYCP_SERVER_HOST}:${TRYCP_SERVER_PORT}`); -test("TryCP Scenario - List everything", async (t) => { - const scenario = await TryCpScenario.create(SERVER_URL); - const alice = await scenario.addPlayerWithHapp([ +test("TryCP Scenario - create a conductor", async (t) => { + const tryCpServer = await TryCpServer.start(); + + const scenario = new TryCpScenario(); + const client = await scenario.addClient(SERVER_URL); + t.ok(client, "client set up"); + + const conductor = await client.addConductor(); + t.ok(conductor.adminWs() && conductor.appWs(), "conductor set up"); + + await scenario.cleanUp(); + await tryCpServer.stop(); +}); + +test("TryCP Scenario - install a hApp to 1 conductor with 1 agent", async (t) => { + const tryCpServer = await TryCpServer.start(); + + const scenario = new TryCpScenario(); + const client = await scenario.addClient(SERVER_URL); + t.ok(client, "client set up"); + + const alice = await scenario.addPlayerWithHapp(client, { + dnas: [{ source: { path: FIXTURE_DNA_URL.pathname } }], + }); + t.ok(alice.conductor, "player alice is associated with a conductor"); + t.equal( + alice.conductor.tryCpClient, + client, + "player alice's conductor is associated with the right client" + ); + + await scenario.cleanUp(); + await tryCpServer.stop(); +}); + +test("TryCP Scenario - install a hApp to 2 conductors with 1 agent each", async (t) => { + const serverPort1 = TRYCP_SERVER_PORT; + const serverPort2 = TRYCP_SERVER_PORT + 1; + const serverUrl1 = new URL(`ws://${TRYCP_SERVER_HOST}:${serverPort1}`); + const serverUrl2 = new URL(`ws://${TRYCP_SERVER_HOST}:${serverPort2}`); + const tryCpServer1 = await TryCpServer.start(serverPort1); + const tryCpServer2 = await TryCpServer.start(serverPort2); + + const scenario = new TryCpScenario(); + const client1 = await scenario.addClient(serverUrl1); + const client2 = await scenario.addClient(serverUrl2); + t.ok(client1, "client 1 set up"); + t.ok(client2, "client 2 set up"); + + const alice = await scenario.addPlayerWithHapp(client1, { + dnas: [{ source: { path: FIXTURE_DNA_URL.pathname } }], + }); + t.equal( + alice.conductor.tryCpClient, + client1, + "player alice's conductor is associated with client 1" + ); + t.ok( + client1.conductors.find((conductor) => conductor === alice.conductor), + "client 1 conductors includes alice's conductor" + ); + + const bob = await scenario.addPlayerWithHapp(client2, { + dnas: [{ source: { path: FIXTURE_DNA_URL.pathname } }], + }); + t.equal( + bob.conductor.tryCpClient, + client2, + "player bob's conductor is associated with client 2" + ); + t.ok( + client2.conductors.find((conductor) => conductor === bob.conductor), + "client 2 conductors includes bob's conductor" + ); + + await scenario.cleanUp(); + await tryCpServer1.stop(); + await tryCpServer2.stop(); +}); + +test("TryCP Scenario - list everything", async (t) => { + const tryCpServer = await TryCpServer.start(); + + const scenario = new TryCpScenario(); + const client = await scenario.addClient(SERVER_URL); + + const alice = await scenario.addPlayerWithHapp(client, [ { path: FIXTURE_DNA_URL.pathname }, ]); const listedApps = await alice.conductor.adminWs().listApps({}); - t.equal(listedApps.length, 1); + t.ok(listedApps.length === 1, "alice's conductor lists 1 installed app"); const listedAppInterfaces = await alice.conductor .adminWs() .listAppInterfaces(); - t.equal(listedAppInterfaces.length, 1); + t.ok( + listedAppInterfaces.length === 1, + "alice's conductor lists 1 app interface" + ); const listCellIds = await alice.conductor.adminWs().listCellIds(); - t.equal(listCellIds.length, 1); + t.ok(listCellIds.length === 1, "alice's conductor lists 1 cell id"); const listedDnas = await alice.conductor.adminWs().listDnas(); - t.equal(listedDnas.length, 1); + t.ok(listedDnas.length === 1, "alice's conductor lists 1 DNA"); await scenario.cleanUp(); + await tryCpServer.stop(); }); -test("TryCP Scenario - Receive signals with 2 conductors", async (t) => { - const scenario = await TryCpScenario.create(SERVER_URL); +test("TryCP Scenario - receive signals with 2 conductors", async (t) => { + const tryCpServer = await TryCpServer.start(); + + const scenario = new TryCpScenario(); + const client = await scenario.addClient(SERVER_URL); let signalHandlerAlice: AppSignalCb | undefined; const signalReceivedAlice = new Promise((resolve) => { @@ -54,21 +142,21 @@ test("TryCP Scenario - Receive signals with 2 conductors", async (t) => { resolve(signal); }; }); - const dnas: DnaSource[] = [{ path: FIXTURE_DNA_URL.pathname }]; - const [alice, bob] = await scenario.addPlayersWithHapps([ + const dnas: Dna[] = [{ source: { path: FIXTURE_DNA_URL.pathname } }]; + const [alice, bob] = await scenario.addPlayersWithHapps(client, [ { dnas, signalHandler: signalHandlerAlice }, { dnas, signalHandler: signalHandlerBob }, ]); const signalAlice = { value: "hello alice" }; alice.cells[0].callZome({ - zome_name: "crud", + zome_name: "coordinator", fn_name: "signal_loopback", payload: signalAlice, }); const signalBob = { value: "hello bob" }; bob.cells[0].callZome({ - zome_name: "crud", + zome_name: "coordinator", fn_name: "signal_loopback", payload: signalBob, }); @@ -77,17 +165,24 @@ test("TryCP Scenario - Receive signals with 2 conductors", async (t) => { signalReceivedAlice, signalReceivedBob, ]); - t.deepEqual(actualSignalAlice.data.payload, signalAlice); - t.deepEqual(actualSignalBob.data.payload, signalBob); + t.deepEqual( + actualSignalAlice.data.payload, + signalAlice, + "received alice's signal" + ); + t.deepEqual(actualSignalBob.data.payload, signalBob, "received bob's signal"); await scenario.cleanUp(); + await tryCpServer.stop(); }); -test("TryCp Scenario - Create and read an entry, 2 conductors", async (t) => { - const scenario = await TryCpScenario.create(SERVER_URL); - t.ok(scenario.uid); +test("TryCp Scenario - create and read an entry, 2 conductors", async (t) => { + const tryCpServer = await TryCpServer.start(); + + const scenario = new TryCpScenario(); + const client = await scenario.addClient(SERVER_URL); - const [alice, bob] = await scenario.addPlayersWithHapps([ + const [alice, bob] = await scenario.addPlayersWithHapps(client, [ [{ path: FIXTURE_DNA_URL.pathname }], [{ path: FIXTURE_DNA_URL.pathname }], ]); @@ -95,7 +190,7 @@ test("TryCp Scenario - Create and read an entry, 2 conductors", async (t) => { const content = "Hi dare"; const createEntryHash = await alice.cells[0].callZome({ - zome_name: "crud", + zome_name: "coordinator", fn_name: "create", payload: content, }); @@ -103,46 +198,166 @@ test("TryCp Scenario - Create and read an entry, 2 conductors", async (t) => { await pause(100); const readContent = await bob.cells[0].callZome({ - zome_name: "crud", + zome_name: "coordinator", fn_name: "read", payload: createEntryHash, }); t.equal(readContent, content); await scenario.cleanUp(); + await tryCpServer.stop(); }); -test("TryCP Scenario - Conductor maintains data after shutdown and restart", async (t) => { - const scenario = await TryCpScenario.create(SERVER_URL); - const [alice, bob] = await scenario.addPlayersWithHapps([ +test("TryCP Scenario - conductor maintains data after shutdown and restart", async (t) => { + const tryCpServer = await TryCpServer.start(); + + const scenario = new TryCpScenario(); + const client = await scenario.addClient(SERVER_URL); + + const [alice, bob] = await scenario.addPlayersWithHapps(client, [ [{ path: FIXTURE_DNA_URL.pathname }], [{ path: FIXTURE_DNA_URL.pathname }], ]); await scenario.shareAllAgents(); + const content = "Before shutdown"; const createEntryHash = await alice.cells[0].callZome({ - zome_name: "crud", + zome_name: "coordinator", fn_name: "create", payload: content, }); await pause(100); const readContent = await bob.cells[0].callZome({ - zome_name: "crud", + zome_name: "coordinator", fn_name: "read", payload: createEntryHash, }); - t.equal(readContent, content); + t.equal( + readContent, + content, + "entry content read successfully before shutdown" + ); await bob.conductor.shutDown(); - await t.rejects(bob.conductor.adminWs().generateAgentPubKey); + await t.rejects( + bob.conductor.adminWs().generateAgentPubKey, + "conductor cannot be reached after shutdown" + ); await bob.conductor.startUp({}); await bob.conductor.connectAppInterface(); const readContentAfterRestart = await bob.cells[0].callZome({ - zome_name: "crud", + zome_name: "coordinator", fn_name: "read", payload: createEntryHash, }); - t.equal(readContentAfterRestart, content); + t.equal( + readContentAfterRestart, + content, + "entry content read successfully after restart" + ); + + await scenario.cleanUp(); + await tryCpServer.stop(); +}); + +test("TryCP Scenario - connect to multiple clients by passing a list of URLs", async (t) => { + const numberOfServers = 2; + const tryCpServers: TryCpServer[] = []; + const serverUrls: URL[] = []; + + for (let i = 0; i < numberOfServers; i++) { + const serverPort = TRYCP_SERVER_PORT + i; + const serverUrl = new URL(`ws://${TRYCP_SERVER_HOST}:${serverPort}`); + const tryCpServer = await TryCpServer.start(serverPort); + tryCpServers.push(tryCpServer); + serverUrls.push(serverUrl); + } + + const scenario = new TryCpScenario(); + await scenario.addClientsPlayers(serverUrls); + t.ok( + scenario.clients.length === numberOfServers, + "scenario has expected number of clients" + ); + + for (const [index, client] of scenario.clients.entries()) { + const PING_MESSAGE = "pingpong"; + const pong = (await client.ping(PING_MESSAGE)).toString(); + t.equal(pong, PING_MESSAGE, `client ${index + 1} is running`); + } + + await scenario.cleanUp(); + await Promise.all(tryCpServers.map((tryCpServer) => tryCpServer.stop())); +}); + +test("TryCP Scenario - create multiple conductors for multiple clients", async (t) => { + const numberOfServers = 2; + const numberOfConductorsPerClient = 3; + const tryCpServers: TryCpServer[] = []; + const serverUrls: URL[] = []; + + for (let i = 0; i < numberOfServers; i++) { + const serverPort = TRYCP_SERVER_PORT + i; + const serverUrl = new URL(`ws://${TRYCP_SERVER_HOST}:${serverPort}`); + const tryCpServer = await TryCpServer.start(serverPort); + tryCpServers.push(tryCpServer); + serverUrls.push(serverUrl); + } + + const scenario = new TryCpScenario(); + await scenario.addClientsPlayers(serverUrls, { + numberOfConductorsPerClient, + }); + + for (const [i, client] of scenario.clients.entries()) { + t.ok( + client.conductors.length === numberOfConductorsPerClient, + `client ${i + 1} has expected number of conductors` + ); + for (const [j, conductor] of client.conductors.entries()) { + const agentPubKey = await conductor.adminWs().generateAgentPubKey(); + t.ok( + agentPubKey, + `conductor ${j + 1} of client ${i + 1} responds with success` + ); + } + } + + await scenario.cleanUp(); + await Promise.all(tryCpServers.map((tryCpServer) => tryCpServer.stop())); +}); + +test("TryCP Scenario - create multiple agents for multiple conductors for multiple clients", async (t) => { + const numberOfServers = 2; + const numberOfConductorsPerClient = 2; + const numberOfAgentsPerConductor = 3; + const tryCpServers: TryCpServer[] = []; + const serverUrls: URL[] = []; + + for (let i = 0; i < numberOfServers; i++) { + const serverPort = TRYCP_SERVER_PORT + i; + const serverUrl = new URL(`ws://${TRYCP_SERVER_HOST}:${serverPort}`); + const tryCpServer = await TryCpServer.start(serverPort); + tryCpServers.push(tryCpServer); + serverUrls.push(serverUrl); + } + + const scenario = new TryCpScenario(); + const clientsPlayers = await scenario.addClientsPlayers(serverUrls, { + numberOfConductorsPerClient, + numberOfAgentsPerConductor, + dnas: [{ source: { path: FIXTURE_DNA_URL.pathname } }], + }); + + for (const [i, clientPlayers] of clientsPlayers.entries()) { + t.ok( + clientPlayers.players.length === + numberOfConductorsPerClient * numberOfAgentsPerConductor, + `client ${i + 1} has expected number of players` + ); + } + await scenario.cleanUp(); + await Promise.all(tryCpServers.map((tryCpServer) => tryCpServer.stop())); });