Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spike test only support #1682

Closed
wants to merge 5 commits into from
Closed

Spike test only support #1682

wants to merge 5 commits into from

Conversation

dtribble
Copy link
Member

@dtribble dtribble commented Sep 3, 2020

Just a sketch to have something concrete to talk about.

Thesed in tests in the economy repository, and
is based on prior code, but generally is not yet tested.
NOTE: this is preliminary only to discuss whether it can
support what we need for testing.
@dtribble
Copy link
Member Author

dtribble commented Sep 3, 2020

Usage in a contract is something like:

   zcf.testOnly(() => ({ collateralKit, sconeKit }));

The test imports the testContext (this should be made easier):

import fakeVatAdmin, { testContext } from '@agoric/zoe/test/unitTests/contracts/fakeVatAdmin';

and then the test can refer to e.g., testContext.zcf and testContext.data.sconeKit.

@dtribble
Copy link
Member Author

dtribble commented Sep 3, 2020

I am interested in how buildRootObject can get access to a name for the contract. With that name, then the test context could then launch multiple contracts and refer to e.g.,

testContext.vault.sconeKit
testContext.vault.zcf
testContext.zoe.offerTables

Copy link
Contributor

@katelynsills katelynsills left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks very promising. Could we see a contract using this, with tests? I could see something like atomicSwap or the secondPriceAuction using this. Does this work under the SwingSet tests? Lastly, I think the name needs to be changed because test.only has a different meaning.

@dtribble dtribble changed the base branch from master to escrowAllTo September 3, 2020 18:34
Copy link
Contributor

@katelynsills katelynsills left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! A few suggestions

packages/zoe/src/contractFacet/types.js Outdated Show resolved Hide resolved
packages/zoe/test/unitTests/contracts/test-zcf.js Outdated Show resolved Hide resolved
packages/zoe/test/unitTests/contracts/zcfTesterContract.js Outdated Show resolved Hide resolved
packages/zoe/src/contractFacet/contractFacet.js Outdated Show resolved Hide resolved
- fix types
- simplify the zcf-test contract
- clean up exports from fakeVatAdmin
Make the testFn default to a no-op function to
support the simple case of making zcf available.
@dtribble
Copy link
Member Author

dtribble commented Sep 4, 2020

Just awaiting @warner 's thoughts on what to do about the argument to buildRootObject .

@warner
Copy link
Member

warner commented Sep 5, 2020

The pattern I'll recommend is one where each component (ZCF, or the contract) is willing to be vulnerable to (and share internal secrets with) a parent who builds it in an unusual way, which we do during tests. The parent indicates that we're in a special environment by either passing an argument that's outside the usual API, or by accessing an export that's outside the usual API. It should be abundantly clear to an auditor/reviewer which is the normal path and which is not.

Vats, like the one created from ZCF, are defined to export a function like export function buildRootObject(vatPowers, vatParameters). For such vats, we can either add a third argument, or have the debug/test pathway go through an additional export.

For contracts, we should start by renaming the primary export, since buildRootObject behaves different anyways. I think @dtribble said invokeStart() would make sense given what ZCF does with it, but buildContract() would work for me too.

We've got the same flexibility with contracts: since ZCF doesn't currently pass any arguments in, we can either define the optional first argument to be this debug/test pathway, or have the contract export a new function for this purpose. Exporting an extra function would be somewhat safer, because if we change ZCF a month from now to pass some new argument in (for functional purposes), and we miss updating a contract, we might leak something important. It's the usual named-vs-positional argument argument.

Then the question is what exactly ZCF and the contract want to share with the test harness, and how to pass them through this new pathway. In both cases the testing-relevant internals aren't created until the primary export function is invoked. I'll assume that all of the interesting internals are created by the time that invocation finishes (and we aren't inventing new bits after it returns, or that we find some different way to return them).

I can think of four patterns (mutable object or not, times exported function vs argument):

1:

export let debugInfo = {};
export function buildRootObject(vatPowers, vatParameters) {
  const root = buildstuff();
  debugInfo.secret = foo;
  return root;
}

2:

let debugInfo = {};
export function getDebugInfo() {
  return debugInfo;
}
export function buildRootObject(vatPowers, vatParameters) {
  const root = buildstuff();
  debugInfo.secret = foo;
  return root;
}

3:

export function buildRootObject(vatPowers, vatParameters, debugInfo={}) {
  const root = buildstuff();
  debugInfo.secret = foo;
  return root;
}

4:

export function buildRootObject(vatPowers, vatParameters, giveDebugInfo) {
  const root = buildstuff();
  let debugInfo = { secret };
  if (giveDebugInfo) {
    giveDebugInfo(debugInfo);
  }
  return root;
}

The new-export cases (1 and 2) need new top-level mutable state, making the module non-pure. I think we tend to avoid that.

The third debugInfo argument (3) would have to be mutable, which also doesn't match our usual coding style. However that might provide a safety feature: if we add a new (non-debug) argument, and we follow our pattern of hardening everything, the code's attempt to add data to the surprisingly-non-mutable argument will fail safely. This pattern has the advantage of simplicity. And it nests nicely; our function-under-test could attach further internal state from other code that it invokes using the same pattern:

export function buildRootObject(vatPowers, vatParameters, debugInfo={}) {
  const root = buildstuff();
  debugInfo.secret = foo;
  debugInfo.contractSecrets = {};
  const contract = makeContract(otherArgs, debugInfo.contractSecrets);
  return root;
}

Using giveDebugInfo as the third argument (4) retains our pattern of hardened arguments and return values. The function can harden the debugInfo object it creates before passing it. It loses a bit of simplicity, because the caller needs to know when giveDebugInfo will be invoked (during buildRootObject? later?), and may need to worry about it being called twice, or not at all.

So I guess I'd give 3 a try, which I think is how you're approaching the contract. I can't quite understand what files are creating what, so I can't tell how close your patch is to what I described above.

When fakeVatAdmin provides a createVat method, the bundle we give to that is the ZCF code, right? The same code that would be used to initialize a dynamic vat in the real system? In that case, it needs to have a signature of buildRootObject(_vatPowers, _vatParameters, debugInfo={}). The test case which creates the fakeVatAdmin should provide it with the (mutable) debugInfo object, retaining a reference for itself. To stick to pure modules, that might mean fakeVatAdmin.js should export a makeFakeVatAdmin(debugInfo) function, instead of exporting a fakeVatAdmin singleton. And setupBasicMints.js should export setup(debugInfo), which calls makeFakeVatAdmin(debugInfo). Then each test should create a const debugInfo = {}, pass it into setup(debugInfo), and then use the contents to poke around inside the code being tested.

@dtribble dtribble changed the base branch from escrowAllTo to master September 9, 2020 18:26
@dtribble dtribble closed this Sep 10, 2020
@dtribble
Copy link
Member Author

This was committed in another PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants