Skip to content

Commit

Permalink
test: satellite upgrade
Browse files Browse the repository at this point in the history
Signed-off-by: David Dal Busco <[email protected]>
  • Loading branch information
peterpeterparker committed Mar 15, 2024
1 parent efda961 commit cff3c6a
Show file tree
Hide file tree
Showing 4 changed files with 250 additions and 28 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@ console.wasm
mission_control.wasm
observatory.wasm
satellite.wasm
satellite-v0.0.15.wasm.gz
out/
90 changes: 62 additions & 28 deletions src/tests/satellite.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,16 @@ import type {
StorageConfig
} from '$declarations/satellite/satellite.did';
import { idlFactory as idlFactorSatellite } from '$declarations/satellite/satellite.factory.did';
import { IDL } from '@dfinity/candid';
import { AnonymousIdentity } from '@dfinity/agent';
import { Ed25519KeyIdentity } from '@dfinity/identity';
import { arrayBufferToUint8Array, toNullable } from '@dfinity/utils';
import { PocketIc, type Actor } from '@hadronous/pic';
import { toArray } from '@junobuild/utils';
import { parse } from '@ltd/j-toml';
import { existsSync, readFileSync } from 'node:fs';
import { join, resolve } from 'node:path';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { afterAll, beforeAll, beforeEach, describe, expect } from 'vitest';

const WASM_PATH_LOCAL = resolve(
process.cwd(),
'.dfx',
'local',
'canisters',
'satellite',
'satellite.wasm'
);

const WASM_PATH_CI = resolve(process.cwd(), 'satellite.wasm.gz');

const WASM_PATH = existsSync(WASM_PATH_CI) ? WASM_PATH_CI : WASM_PATH_LOCAL;
import { WASM_PATH, satelliteInitArgs } from './utils/satellite-tests.utils';

describe('Satellite', () => {
let pic: PocketIc;
Expand All @@ -35,19 +24,11 @@ describe('Satellite', () => {
beforeAll(async () => {
pic = await PocketIc.create();

const arg = IDL.encode(
[
IDL.Record({
controllers: IDL.Vec(IDL.Principal)
})
],
[{ controllers: [controller.getPrincipal()] }]
);

const { actor: c } = await pic.setupCanister<SatelliteActor>({
idlFactory: idlFactorSatellite,
wasm: WASM_PATH,
arg
arg: satelliteInitArgs(controller),
sender: controller.getPrincipal()
});

actor = c;
Expand All @@ -74,7 +55,7 @@ describe('Satellite', () => {
});

it('should create a collection', async () => {
const { set_rule, list_rules, list_controllers } = actor;
const { set_rule, list_rules } = actor;

await set_rule({ Db: null }, 'test', setRule);

Expand All @@ -93,7 +74,7 @@ describe('Satellite', () => {
});

it('should list collections', async () => {
const { list_rules, set_rule } = actor;
const { list_rules } = actor;

const [
[collection, { updated_at, created_at, memory, mutable_permissions, read, write }],
Expand All @@ -104,6 +85,7 @@ describe('Satellite', () => {
expect(memory).toEqual(toNullable({ Heap: null }));
expect(read).toEqual({ Managed: null });
expect(write).toEqual({ Managed: null });
expect(mutable_permissions).toEqual([true]);
expect(created_at).toBeGreaterThan(0n);
expect(updated_at).toBeGreaterThan(0n);
});
Expand Down Expand Up @@ -445,6 +427,58 @@ describe('Satellite', () => {
});
});

describe('user', () => {
const user = Ed25519KeyIdentity.generate();

beforeAll(() => {
actor.setIdentity(user);
});

it('should create a user', async () => {
const { set_doc, list_docs } = actor;

await set_doc('#user', user.getPrincipal().toText(), {
data: await toArray({
provider: 'internet_identity'
}),
description: toNullable(),
updated_at: toNullable()
});

const { items: users } = await list_docs('#user', {
matcher: toNullable(),
order: toNullable(),
owner: toNullable(),
paginate: toNullable()
});

expect(users).toHaveLength(1);
expect(users.find(([key]) => key === user.getPrincipal().toText())).not.toBeUndefined();
});
});

describe('anonymous', () => {
beforeAll(() => {
actor.setIdentity(new AnonymousIdentity());
});

it('should create a user', async () => {
const { set_doc } = actor;

const user = Ed25519KeyIdentity.generate();

await expect(
set_doc('#user', user.getPrincipal().toText(), {
data: await toArray({
provider: 'internet_identity'
}),
description: toNullable(),
updated_at: toNullable()
})
).rejects.toThrow('Cannot write.');
});
});

describe('hacking', () => {
const hacker = Ed25519KeyIdentity.generate();

Expand Down
126 changes: 126 additions & 0 deletions src/tests/satellite.upgrade.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import type { _SERVICE as SatelliteActor } from '$declarations/satellite/satellite.did';
import { idlFactory as idlFactorSatellite } from '$declarations/satellite/satellite.factory.did';
import type { Identity } from '@dfinity/agent';
import { Ed25519KeyIdentity } from '@dfinity/identity';
import type { Principal } from '@dfinity/principal';
import { toNullable } from '@dfinity/utils';
import { PocketIc, type Actor } from '@hadronous/pic';
import { toArray } from '@junobuild/utils';
import { afterEach, beforeEach, describe, expect } from 'vitest';
import {
WASM_PATH,
WASM_PATH_V0_0_15,
downloadSatelliteV0_0_15,
satelliteInitArgs
} from './utils/satellite-tests.utils';

describe('satellite upgrade v0.0.16', () => {
let pic: PocketIc;
let actor: Actor<SatelliteActor>;
let canisterId: Principal;

const controller = Ed25519KeyIdentity.generate();

beforeEach(async () => {
pic = await PocketIc.create();

await downloadSatelliteV0_0_15();

const { actor: c, canisterId: cId } = await pic.setupCanister<SatelliteActor>({
idlFactory: idlFactorSatellite,
wasm: WASM_PATH_V0_0_15,
arg: satelliteInitArgs(controller),
sender: controller.getPrincipal()
});

actor = c;
canisterId = cId;
actor.setIdentity(controller);
});

afterEach(async () => {
await pic?.tearDown();
});

const upgrade = async () => {
// Prevent Error: Canister lxzze-o7777-77777-aaaaa-cai is rate limited because it executed too many instructions in the previous install_code messages. Please retry installation after several minutes.
for (let i = 0; i < 100; i++) {
await pic.tick();
}

await pic.upgradeCanister({
canisterId,
wasm: WASM_PATH,
sender: controller.getPrincipal()
});
};

const initUsers = async (): Promise<Identity[]> => {
const { set_doc } = actor;

const user1 = Ed25519KeyIdentity.generate();

await set_doc('#user', user1.getPrincipal().toText(), {
data: await toArray({
provider: 'internet_identity'
}),
description: toNullable(),
updated_at: toNullable()
});

const user2 = Ed25519KeyIdentity.generate();

await set_doc('#user', user2.getPrincipal().toText(), {
data: await toArray({
provider: 'internet_identity'
}),
description: toNullable(),
updated_at: toNullable()
});

return [user1, user2];
};

const testUsers = async (users: Identity[]) => {
const { list_docs } = actor;

const { items } = await list_docs('#user', {
matcher: toNullable(),
order: toNullable(),
owner: toNullable(),
paginate: toNullable()
});

expect(users).toHaveLength(users.length);

for (const user of users) {
expect(items.find(([key]) => key === user.getPrincipal().toText())).not.toBeUndefined();
}
};

it('should still list users from heap', async () => {
await initUsers();

const users = await initUsers();

await testUsers(users);

await upgrade();

await testUsers(users);
});

it('should add users after upgrade and still list all users from heap', async () => {
await initUsers();

const users = await initUsers();

await testUsers(users);

await upgrade();

const moreUsers = await initUsers();

await testUsers([...users, ...moreUsers]);
});
});
61 changes: 61 additions & 0 deletions src/tests/utils/satellite-tests.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import type { Identity } from '@dfinity/agent';
import { IDL } from '@dfinity/candid';
import { nonNullish } from '@dfinity/utils';
import { existsSync, writeFileSync } from 'node:fs';
import { get, type RequestOptions } from 'node:https';
import { resolve } from 'node:path';

const WASM_PATH_LOCAL = resolve(
process.cwd(),
'.dfx',
'local',
'canisters',
'satellite',
'satellite.wasm'
);

const WASM_PATH_CI = resolve(process.cwd(), 'satellite.wasm.gz');

export const WASM_PATH = existsSync(WASM_PATH_CI) ? WASM_PATH_CI : WASM_PATH_LOCAL;

export const satelliteInitArgs = (controller: Identity): ArrayBuffer =>
IDL.encode(
[
IDL.Record({
controllers: IDL.Vec(IDL.Principal)
})
],
[{ controllers: [controller.getPrincipal()] }]
);

const downloadFromURL = async (url: string | RequestOptions): Promise<Buffer> => {
return await new Promise((resolve, reject) => {
get(url, async (res) => {
if (nonNullish(res.statusCode) && [301, 302].includes(res.statusCode)) {
await downloadFromURL(res.headers.location!).then(resolve, reject);
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const data: any[] = [];

res.on('data', (chunk) => data.push(chunk));
res.on('end', () => {
resolve(Buffer.concat(data));
});
res.on('error', reject);
});
});
};

export const WASM_PATH_V0_0_15 = resolve(process.cwd(), 'satellite-v0.0.15.wasm.gz');

export const downloadSatelliteV0_0_15 = async () => {
if (existsSync(WASM_PATH_V0_0_15)) {
return;
}

const buffer = await downloadFromURL(
'https://github.com/junobuild/juno/releases/download/v0.0.26/satellite-v0.0.15.wasm.gz'
);
writeFileSync(WASM_PATH_V0_0_15, buffer);
};

0 comments on commit cff3c6a

Please sign in to comment.