Skip to content

Commit

Permalink
test(vats): no test-only code is in production configs
Browse files Browse the repository at this point in the history
  • Loading branch information
dckc committed Mar 3, 2023
1 parent 1b9a27a commit 37c10aa
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 18 deletions.
5 changes: 5 additions & 0 deletions packages/inter-protocol/src/proposals/demoIssuers.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
import { E, Far } from '@endo/far';
import { Stake, Stable } from '@agoric/vats/src/tokens.js';
import { Nat } from '@endo/nat';
import { notForProductionUse } from '@agoric/internal/src/test-only-magic-cookie.js';

const { Fail, quote: q } = assert;
const { multiply, floorDivide } = natSafeMath;
Expand Down Expand Up @@ -177,6 +178,8 @@ const run2places = f =>
BigInt(Math.round(f * 100)) * 10n ** BigInt(DecimalPlaces[Stable.symbol] - 2);

/**
* WARNING: not for production use
*
* @param {bigint} value
* @param {{
* centralSupplyInstall: ERef<Installation>,
Expand All @@ -189,6 +192,7 @@ const mintRunPayment = async (
value,
{ centralSupplyInstall, feeMintAccess: feeMintAccessP, zoe },
) => {
notForProductionUse();
const feeMintAccess = await feeMintAccessP;

const { creatorFacet: ammSupplier } = await E(zoe).startInstance(
Expand Down Expand Up @@ -295,6 +299,7 @@ export const connectFaucet = async ({
const { issuer, brand, mint } = await provideIssuerKit(issuerName);
const unit = 10n ** BigInt(DecimalPlaces[issuerName]);
const amount = AmountMath.make(brand, Nat(record.balance) * unit);
notForProductionUse();
const payment = await E(mint).mintPayment(amount);

/** @type {UserPaymentRecord[]} */
Expand Down
9 changes: 9 additions & 0 deletions packages/internal/src/test-only-magic-cookie.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const cookie = harden({});

/**
* Facilitate static analysis to prevent
* demo/test facilities from being bundled in production.
*/
export const notForProductionUse = () => {
return cookie;
};
8 changes: 6 additions & 2 deletions packages/vats/src/vat-mints.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Far } from '@endo/far';
import { makeIssuerKit, AmountMath } from '@agoric/ertp';

import { makeScalarMapStore } from '@agoric/store';
import { notForProductionUse } from '@agoric/internal/src/test-only-magic-cookie.js';

// This vat contains two starting mints for demos: moolaMint and
// simoleanMint.
Expand All @@ -21,13 +22,16 @@ export function buildRootObject() {
getIssuers: issuerNames => issuerNames.map(api.getIssuer),

/**
* NOTE: a mint is ability to mint new digital assets,
* WARNING: a mint is ability to mint new digital assets,
* a very powerful authority that is usually closely held.
* But this mint is for demo / faucet purposes.
*
* @param {string} name
*/
getMint: name => mintsAndBrands.get(name).mint,
getMint: name => {
notForProductionUse();
return mintsAndBrands.get(name).mint;
},
/** @param {string[]} issuerNames */
getMints: issuerNames => issuerNames.map(api.getMint),
/**
Expand Down
79 changes: 63 additions & 16 deletions packages/vats/test/test-boot-config.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
// @ts-check
import { test } from '@agoric/swingset-vat/tools/prepare-test-env-ava.js';
import { test as anyTest } from '@agoric/swingset-vat/tools/prepare-test-env-ava.js';

import { promises as fsPromises } from 'fs';
import path from 'path';

import { mustMatch } from '@agoric/store';
import { shape as ssShape } from '@agoric/swingset-vat';
import { loadSwingsetConfigFile, shape as ssShape } from '@agoric/swingset-vat';
import { makeNodeBundleCache } from '@agoric/swingset-vat/tools/bundleTool.js';
import { ParametersShape as BootParametersShape } from '../src/core/boot-psm.js';

/** @type {import('ava').TestFn<Awaited<ReturnType<typeof makeTestContext>>>} */
const test = anyTest;

const CONFIG_FILES = [
'decentral-core-config.json',
'decentral-demo-config.json',
Expand All @@ -18,34 +22,77 @@ const CONFIG_FILES = [
'decentral-test-vaults-config.json',
];

/**
* @typedef {{
* asset: (...ps: string[]) => Promise<string>,
* }} Context
* @typedef {import('ava').ExecutionContext<Context> } TestCtx
*/
const PROD_CONFIG_FILES = [
'decentral-main-psm-config.json',
'decentral-psm-config.json',
'decentral-test-vaults-config.json',
];

// NOTE: confine ambient authority to test.before
test.before(t => {
// #region NOTE: confine ambient authority to test.before
const makeTestContext = async () => {
const pathname = new URL(import.meta.url).pathname;
const dirname = path.dirname(pathname);
const asset = (...ps) =>
fsPromises.readFile(path.join(dirname, ...ps), 'utf-8');
t.context = { asset };
const resolve = (...ps) => path.join(dirname, ...ps);
const asset = (...ps) => fsPromises.readFile(resolve(...ps), 'utf-8');

const cacheDir = resolve('..', 'bundles');
const bundleCache = await makeNodeBundleCache(cacheDir, {}, s => import(s));

return { asset, bundleCache, cacheDir, resolve, basename: path.basename };
};

test.before(async t => {
t.context = await makeTestContext();
});
// #endregion

test('Bootstrap SwingSet config file syntax', /** @param {TestCtx} t */ async t => {
test('Bootstrap SwingSet config file syntax', async t => {
const { asset } = t.context;

await Promise.all(
CONFIG_FILES.map(async f => {
const txt = await asset('..', f);
const config = harden(JSON.parse(txt));
await t.notThrows(() => mustMatch(config, ssShape.SwingSetConfig), f);
t.notThrows(() => mustMatch(config, ssShape.SwingSetConfig), f);
const parameters = config?.vats?.bootstrap?.parameters;
t.log('syntax check:', f, parameters ? 'and parameters' : '');
(await parameters) &&
parameters &&
t.notThrows(() => mustMatch(parameters, BootParametersShape), f);
}),
);
});

test('no test-only code is in production configs', async t => {
const { basename, bundleCache, resolve } = t.context;
const { entries } = Object;

const seen = new Set();

const noLog = () => {};

for await (const configSpec of PROD_CONFIG_FILES) {
t.log('checking config', configSpec);
const fullPath = resolve('..', configSpec);
const config = await loadSwingsetConfigFile(fullPath);
if (!config) throw t.truthy(config, configSpec); // if/throw refines type
const { bundles } = config;
if (!bundles) throw t.truthy(bundles, configSpec);

for await (const [name, spec] of entries(bundles)) {
if (!('sourceSpec' in spec)) throw t.fail();
await bundleCache.load(spec.sourceSpec, undefined, noLog);
const targetName = basename(spec.sourceSpec, '.js');
if (seen.has(targetName)) return;
seen.add(targetName);
t.log('checking bundle', targetName);
const meta = await bundleCache.validate(targetName);
t.truthy(meta, name);

for (const item of meta.contents) {
if (item.relativePath.includes('test-only-magic-cookie')) {
t.fail(`${configSpec} bundle ${name}: ${item.relativePath}`);
}
}
}
}
});

0 comments on commit 37c10aa

Please sign in to comment.