Skip to content
This repository has been archived by the owner on Oct 5, 2019. It is now read-only.

Switch to Accord Project's Concerto (0.80) #23

Closed
jeromesimeon opened this issue Sep 16, 2019 · 5 comments
Closed

Switch to Accord Project's Concerto (0.80) #23

jeromesimeon opened this issue Sep 16, 2019 · 5 comments

Comments

@jeromesimeon
Copy link
Member

Switching Concerto tools to the new @accordproject/concerto runs afoul of the recent changes to remove instanceof. (See issue accordproject/concerto#47)

I believe those create confusion for the sinon-based testing. Sample trace:

bash-3.2$ npm run test

> composer-concerto-tools@0.61.1 test /Users/jeromesimeon/git/concerto-tools
> nyc mocha --recursive -t 10000



  GoLangVisitor
    visit
      1) should call visitModelManager for a ModelManager


  0 passing (82ms)
  1 failing

  1) GoLangVisitor
       visit
         should call visitModelManager for a ModelManager:
     Unrecognised type: object, value: ModelManager {
  [accept]: 
   { [Function: proxy]
     [length]: 2,
     [name]: 'proxy',
     [prototype]: 
      functionStub {
        [constructor]: 
         { [Function: functionStub]
           [length]: 0,
           [name]: 'functionStub',
           [prototype]: [Circular],
           id: 'stub#10' } },
     isSinonProxy: true,
     formatters: 
      { c: 
         { [Function: c]
           [length]: 1,
           [name]: 'c',
           [prototype]: c { [constructor]: [Circular] } },
        n: 
         { [Function: n]
           [length]: 1,
           [name]: 'n',
           [prototype]: n { [constructor]: [Circular] } },
        D: 
         { [Function: D]
           [length]: 2,
           [name]: 'D',
           [prototype]: D { [constructor]: [Circular] } },
        C: 
         { [Function: C]
           [length]: 1,
           [name]: 'C',
           [prototype]: C { [constructor]: [Circular] } },
        t: 
         { [Function: t]
           [length]: 1,
           [name]: 't',
           [prototype]: t { [constructor]: [Circular] } },
        '*': 
         { [Function: *]
           [length]: 2,
           [name]: '*',
           [prototype]: * { [constructor]: [Circular] } } },
     reset: 
      { [Function: reset]
        [length]: 0,
        [name]: 'reset',
        [prototype]: reset { [constructor]: [Circular] } },
     invoke: 
      { [Function: invoke]
        [length]: 3,
        [name]: 'invoke',
        [prototype]: invoke { [constructor]: [Circular] } },
     named: 
      { [Function: named]
        [length]: 1,
        [name]: 'named',
        [prototype]: named { [constructor]: [Circular] } },
     getCall: 
      { [Function: getCall]
        [length]: 1,
        [name]: 'getCall',
        [prototype]: getCall { [constructor]: [Circular] } },
     getCalls: 
      { [Function: getCalls]
        [length]: 0,
        [name]: 'getCalls',
        [prototype]: getCalls { [constructor]: [Circular] } },
     calledBefore: 
      { [Function: calledBefore]
        [length]: 1,
        [name]: 'calledBefore',
        [prototype]: calledBefore { [constructor]: [Circular] } },
     calledAfter: 
      { [Function: calledAfter]
        [length]: 1,
        [name]: 'calledAfter',
        [prototype]: calledAfter { [constructor]: [Circular] } },
     calledImmediatelyBefore: 
      { [Function: calledImmediatelyBefore]
        [length]: 1,
        [name]: 'calledImmediatelyBefore',
        [prototype]: calledImmediatelyBefore { [constructor]: [Circular] } },
     calledImmediatelyAfter: 
      { [Function: calledImmediatelyAfter]
        [length]: 1,
        [name]: 'calledImmediatelyAfter',
        [prototype]: calledImmediatelyAfter { [constructor]: [Circular] } },
     withArgs: 
      { [Function: withArgs]
        [length]: 0,
        [name]: 'withArgs',
        [prototype]: withArgs { [constructor]: [Circular] } },
     matchingFakes: 
      { [Function: matchingFakes]
        [length]: 2,
        [name]: 'matchingFakes',
        [prototype]: matchingFakes { [constructor]: [Circular] } },
     matches: 
      { [Function: matches]
        [length]: 2,
        [name]: 'matches',
        [prototype]: matches { [constructor]: [Circular] } },
     printf: 
      { [Function: printf]
        [length]: 1,
        [name]: 'printf',
        [prototype]: printf { [constructor]: [Circular] } },
     calledOn: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     alwaysCalledOn: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     calledWith: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     calledWithMatch: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     alwaysCalledWith: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     alwaysCalledWithMatch: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     calledWithExactly: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     alwaysCalledWithExactly: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     neverCalledWith: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     neverCalledWithMatch: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     threw: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     alwaysThrew: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     returned: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     alwaysReturned: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     calledWithNew: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     alwaysCalledWithNew: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     callArg: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     callArgWith: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     callArgOn: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     callArgOnWith: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     throwArg: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     yield: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     invokeCallback: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     yieldOn: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     yieldTo: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     yieldToOn: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     spyCall: 
      { [Function: createSpyCall]
        [length]: 7,
        [name]: 'createSpyCall',
        [prototype]: createSpyCall { [constructor]: [Circular] },
        toString: 
         { [Function: toString]
           [length]: 0,
           [name]: 'toString',
           [prototype]: toString { [constructor]: [Circular] } } },
     id: 'spy#10',
     called: false,
     notCalled: true,
     calledOnce: false,
     calledTwice: false,
     calledThrice: false,
     callCount: 0,
     firstCall: null,
     secondCall: null,
     thirdCall: null,
     lastCall: null,
     args: [ [length]: 0 ],
     returnValues: [ [length]: 0 ],
     thisValues: [ [length]: 0 ],
     exceptions: [ [length]: 0 ],
     callIds: [ [length]: 0 ],
     errorsWithCallStack: [ [length]: 0 ],
     displayName: 'accept',
     toString: 
      { [Function: toString]
        [length]: 0,
        [name]: 'toString',
        [prototype]: toString { [constructor]: [Circular] } },
     instantiateFake: 
      { [Function: create]
        [length]: 1,
        [name]: 'create',
        [prototype]: create { [constructor]: [Circular] } },
     func: 
      { [Function: functionStub]
        [length]: 0,
        [name]: 'functionStub',
        [prototype]: functionStub { [constructor]: [Circular] },
        id: 'stub#10' },
     createStubInstance: 
      { [Function]
        [length]: 1,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     callsFake: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     callsArg: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     callsArgOn: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     callsArgWith: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     callsArgOnWith: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     usingPromise: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     yields: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     yieldsRight: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     yieldsOn: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     yieldsTo: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     yieldsToOn: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     throws: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     throwsException: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     returns: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     returnsArg: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     throwsArg: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     returnsThis: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     resolves: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     rejects: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     callThrough: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     get: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     set: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     value: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     callsArgAsync: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     callsArgOnAsync: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     callsArgWithAsync: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     callsArgOnWithAsync: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     yieldsAsync: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     yieldsRightAsync: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     yieldsOnAsync: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     yieldsToAsync: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     yieldsToOnAsync: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     create: 
      { [Function: create]
        [length]: 1,
        [name]: 'create',
        [prototype]: create { [constructor]: [Circular] } },
     resetBehavior: 
      { [Function: resetBehavior]
        [length]: 0,
        [name]: 'resetBehavior',
        [prototype]: resetBehavior { [constructor]: [Circular] } },
     resetHistory: 
      { [Function: reset]
        [length]: 0,
        [name]: 'reset',
        [prototype]: reset { [constructor]: [Circular] } },
     onCall: 
      { [Function: onCall]
        [length]: 1,
        [name]: 'onCall',
        [prototype]: onCall { [constructor]: [Circular] } },
     onFirstCall: 
      { [Function: onFirstCall]
        [length]: 0,
        [name]: 'onFirstCall',
        [prototype]: onFirstCall { [constructor]: [Circular] } },
     onSecondCall: 
      { [Function: onSecondCall]
        [length]: 0,
        [name]: 'onSecondCall',
        [prototype]: onSecondCall { [constructor]: [Circular] } },
     onThirdCall: 
      { [Function: onThirdCall]
        [length]: 0,
        [name]: 'onThirdCall',
        [prototype]: onThirdCall { [constructor]: [Circular] } },
     isPresent: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     addBehavior: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     createBehavior: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] } },
     defaultBehavior: null,
     behaviors: [ [length]: 0 ],
     rootObj: [Circular],
     propName: 'accept',
     restore: 
      { [Function]
        [length]: 0,
        [name]: '',
        [prototype]: { [constructor]: [Circular] },
        sinon: true },
     stackTrace: 'Error: Stack Trace for original\n    at wrapMethod (/Users/jeromesimeon/git/concerto-tools/node_modules/sinon/lib/sinon/util/core/wrap-method.js:110:26)\n    at stub (/Users/jeromesimeon/git/concerto-tools/node_modules/sinon/lib/sinon/stub.js:58:44)\n    at /Users/jeromesimeon/git/concerto-tools/node_modules/sinon/lib/sinon/stub-entire-object.js:15:13\n    at /Users/jeromesimeon/git/concerto-tools/node_modules/sinon/lib/sinon/util/core/walk.js:23:22\n    at Array.forEach (<anonymous>)\n    at walkInternal (/Users/jeromesimeon/git/concerto-tools/node_modules/sinon/lib/sinon/util/core/walk.js:18:37)\n    at walkInternal (/Users/jeromesimeon/git/concerto-tools/node_modules/sinon/lib/sinon/util/core/walk.js:29:9)\n    at walk (/Users/jeromesimeon/git/concerto-tools/node_modules/sinon/lib/sinon/util/core/walk.js:44:12)\n    at stubEntireObject (/Users/jeromesimeon/git/concerto-tools/node_modules/sinon/lib/sinon/stub-entire-object.js:7:5)\n    at stub (/Users/jeromesimeon/git/concerto-tools/node_modules/sinon/lib/sinon/stub.js:35:16)\n    at Object.stub.createStubInstance (/Users/jeromesimeon/git/concerto-tools/node_modules/sinon/lib/sinon/stub.js:65:12)\n    at Context.it.only (/Users/jeromesimeon/git/concerto-tools/test/codegen/fromcto/golang/golangvisitor.js:51:31)\n    at callFn (/Users/jeromesimeon/git/concerto-tools/node_modules/mocha/lib/runnable.js:372:21)\n    at Test.Runnable.run (/Users/jeromesimeon/git/concerto-tools/node_modules/mocha/lib/runnable.js:364:7)\n    at Runner.runTest (/Users/jeromesimeon/git/concerto-tools/node_modules/mocha/lib/runner.js:455:10)\n    at /Users/jeromesimeon/git/concerto-tools/node_modules/mocha/lib/runner.js:573:12\n    at next (/Users/jeromesimeon/git/concerto-tools/node_modules/mocha/lib/runner.js:369:14)\n    at /Users/jeromesimeon/git/concerto-tools/node_modules/mocha/lib/runner.js:379:7\n    at next (/Users/jeromesimeon/git/concerto-tools/node_modules/mocha/lib/runner.js:303:14)\n    at /Users/jeromesimeon/git/concerto-tools/node_modules/mocha/lib/runner.js:342:7\n    at done (/Users/jeromesimeon/git/concerto-tools/node_modules/mocha/lib/runnable.js:319:5)\n    at callFn (/Users/jeromesimeon/git/concerto-tools/node_modules/mocha/lib/runnable.js:395:7)\n    at Hook.Runnable.run (/Users/jeromesimeon/git/concerto-tools/node_modules/mocha/lib/runnable.js:364:7)\n    at next (/Users/jeromesimeon/git/concerto-tools/node_modules/mocha/lib/runner.js:317:10)\n    at Immediate.<anonymous> (/Users/jeromesimeon/git/concerto-tools/node_modules/mocha/lib/runner.js:347:5)\n    at runCallback (timers.js:810:20)\n    at tryOnImmediate (timers.js:768:5)\n    at processImmediate [as _immediateCallback] (timers.js:745:5)',
     wrappedMethod: { [Function: accept] [length]: 2, [name]: 'accept' } },
...
@jeromesimeon
Copy link
Member Author

I'm suspecting the error is triggered by this line in the latest Concerto code:
https://github.com/accordproject/concerto/blob/ad309eaf207f8e57f219d30dd574970e694f2e77/lib/modelmanager.js#L641

@jeromesimeon
Copy link
Member Author

The new Concerto code seems to make use of https://joshmiller.cc/symbol-hasinstance/

@jeromesimeon
Copy link
Member Author

jeromesimeon commented Sep 16, 2019

A slightly more specific analysis of what I think the problem is, and a proposed fix.

Why are tests failing:

  1. the behaviour for instanceof in Concerto classes (e.g., for ModelManager) is overridden using the idiom here: https://github.com/accordproject/concerto/blob/ad309eaf207f8e57f219d30dd574970e694f2e77/lib/modelmanager.js#L641
    which means it relies on the presence of a _isModelManager field in the object instance.
  2. sinon's createStubInstance method does not call the constructors which means _isModelManager is not initialised.
  3. as a result tests relying on instanceof (e.g., visitors) will always return false

So I believe the support for instanceof should work as expected when using the library, but will fail in the mocked tests.

I could not find a very official way to mock the constructor through sinon in Stack overflow or in the Sinon documentation. However, the following does work:
Instead of:

let thing = sinon.createStubInstance(ModelManager);

in the tests, use:

let thing = sinon.createStubInstance(ModelManager);
thing._isModelManager = true;

Better suggestions to address this are welcome.

@mttrbrts
Copy link
Sponsor Member

This is sound IMO. The concerto module includes test coverage for the usual unstubbed case.

@jeromesimeon
Copy link
Member Author

This is sound IMO. The concerto module includes test coverage for the usual unstubbed case.

Thanks so much for the quick feedback. Corresponding fixes are in PR #24

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

No branches or pull requests

2 participants