describe(scenario.description, (): void => {
				let expectedVersionStub: SinonStub;

				beforeEach((): void => {
					DatabaseService["initialVersion"] = scenario.initialVersion;
					if (scenario.expectedVersion) {
						expectedVersionStub = sinon.stub(DatabaseService, "expectedVersion" as keyof DatabaseService).get((): DOMString => String(scenario.expectedVersion));
					}
				});

				describe("fail", (): void => {
					beforeEach((): void => {
						database.commit = false;
						DatabaseService["startUpgrade"](errorCallback);
					});

					it("should determine the upgrades to apply", (): Chai.Assertion => DatabaseService["upgradesToApply"].should.deep.equal(DatabaseService["upgrades"].slice(scenario.startIndex, scenario.endIndex)));
					it("should change the database version", (): Chai.Assertion => database.changeVersion.should.have.been.calledWith(scenario.initialVersion, DatabaseService["expectedVersion"], sinon.match.func));
					it("should invoke the first upgrade handler", (): Chai.Assertion => nextUpgradeStub.should.have.been.calledOnce);
					it("should invoke the error callback", (): Chai.Assertion => errorCallback.should.have.been.called);
					it("should not determine that the version is OK", (): Chai.Assertion => versionOkStub.should.not.have.been.called);
				});

				describe("success", (): void => {
					beforeEach((): void => DatabaseService["startUpgrade"](errorCallback));

					it("should determine the upgrades to apply", (): Chai.Assertion => DatabaseService["upgradesToApply"].should.deep.equal(DatabaseService["upgrades"].slice(scenario.startIndex, scenario.endIndex)));
					it("should change the database version", (): Chai.Assertion => database.changeVersion.should.have.been.calledWith(scenario.initialVersion, DatabaseService["expectedVersion"], sinon.match.func));
					it("should invoke the first upgrade handler", (): Chai.Assertion => nextUpgradeStub.should.have.been.calledOnce);
					it("should determine that the version is OK", (): Chai.Assertion => versionOkStub.should.have.been.called);
					it("should not invoke the error callback", (): Chai.Assertion => errorCallback.should.not.have.been.called);
				});

				afterEach((): void => {
					if (expectedVersionStub) {
						expectedVersionStub.restore();
					}
				});
			});
Example #2
0
  describe('attachAssets', () => {
    let modelFindAllStub: SinonStub;
    beforeEach(() => {
      modelFindAllStub = sandbox.stub(delegatesModel, 'findAll');
    });
    it('should do do nothing if result is empty', async () => {
      modelFindAllStub.resolves([]);
      await instance.attachAssets([]);
    });
    it('should throw if a tx was provided but not returned by model.findAll', async () => {
      modelFindAllStub.resolves([]);
      await expect(instance.attachAssets([{id: 'ciao'}] as any))
        .rejectedWith('Couldn\'t restore asset for Delegate tx: ciao');
    });
    it('should use model result and modify original arr', async () => {
      modelFindAllStub.resolves([
        { transactionId: 2, username: '******'},
        { transactionId: 1, username: '******'},
      ]);
      const txs: any = [{id: 1}, {id: 2}];

      await instance.attachAssets(txs);

      expect(txs[0]).deep.eq({
        id: 1, asset: {
          delegate: {
            username: '******'
          },
        },
      });
      expect(txs[1]).deep.eq({
        id: 2, asset: {
          delegate: {
            username: '******'
          },
        },
      });
    });
  });
Example #3
0
        it('should discover incoming links for resources', async () => {
            // given
            fetchResource.withArgs('http://example.com/resource')
                .returns(mockedResponse({
                    xhrBuilder: responseBuilder().body(Bodies.someJsonLd),
                }));

            // when
            const hydraRes = await Hydra.loadResource('http://example.com/resource');
            const res = hydraRes.get('http://example.com/resource');
            const incomingLinks = res['http://example.com/vocab#other']._links;

            // then
            expect(incomingLinks.length).toBe(2);
            expect(
                _.some(incomingLinks, {
                    predicate: 'http://example.com/vocab#other',
                    subjectId: 'http://example.com/resource' })).toBe(true);
            expect(_.some(incomingLinks, {
                predicate: 'http://example.com/vocab#other_yet',
                subjectId: 'http://example.com/resource'  })).toBe(true);
        });
        context('as factory', function() {
          let ClassWithDecoratedDependency: any,
            classWithDecoratedDependencyConstructorSpy: SinonSpy,
            ProvidedDependencyToken: symbol,
            factoryProvidedDependency: any,
            factoryProvidedDependencyStub: SinonStub;

          beforeEach(function() {
            let classMock = require('./mocks/classWithDecoratedDependency.mock');
            ClassWithDecoratedDependency = classMock.ClassWithDecoratedDependency;
            classWithDecoratedDependencyConstructorSpy = classMock.classWithDecoratedDependencyConstructorSpy;

            let providedDependencyMock = require('./mocks/providedDependency.mock');
            ProvidedDependencyToken = providedDependencyMock.ProvidedDependencyToken;
            factoryProvidedDependency = providedDependencyMock.factoryProvidedDependency;
            factoryProvidedDependencyStub = providedDependencyMock.factoryProvidedDependencyStub;

            factoryProvidedDependencyStub.returns(factoryProvidedDependency);

            provide(ProvidedDependencyToken, {
              useFactory: factoryProvidedDependencyStub
            });

            bootstrap(ClassWithDecoratedDependency);
          });

          it('should instantiate the class', function() {
            expect(classWithDecoratedDependencyConstructorSpy.calledOnce).to.be.true;
          });

          it('should instantiate the class with one parameter', function() {
            expect(classWithDecoratedDependencyConstructorSpy.firstCall.args.length).to.equal(1);
          });

          it('should instantiate the class with an instance of the dependency type', function() {
            expect(classWithDecoratedDependencyConstructorSpy.firstCall.args[0]).to.equal(factoryProvidedDependency);
          });
        });
Example #5
0
    it('does not retry when doRetry returns false', function(done) {
      const response = new Response(null, { status: 500 });
      fetch.returns(Promise.resolve(response));

      const doRetryCallback = stub().returns(false);

      client.configure(config => config.rejectErrorResponses().withRetry({
        maxRetries: 2,
        interval: 10,
        doRetry: doRetryCallback
      }));
      client.fetch('path')
        .then((r) => {
          done('fetch did not error');
        })
        .catch((r) => {
          // 1 original call plus 0 retries
          expect(fetch).to.have.callCount(1);
          // only called on retries
          expect(doRetryCallback).to.have.callCount(1);
          done();
        });
    });
Example #6
0
    it('should wrap all ops within tx', async () => {
      const preStub  = sandbox.stub();
      const postStub = sandbox.stub();
      txStub.callsFake(async (t) => {
        preStub();
        await t('tx');
        postStub();
      });

      await inst.applyBlock(block, false, true, accountsMap);
      expect(preStub.called).is.true;
      expect(postStub.called).is.true;
      expect(preStub.calledBefore(txLogic.stubs.applyUnconfirmed)).is.true;
      expect(preStub.calledBefore(txLogic.stubs.apply)).is.true;
      expect(preStub.calledBefore(saveBlockStub)).is.true;
      expect(preStub.calledBefore(roundsModule.stubs.tick)).is.true;

      expect(postStub.calledAfter(txLogic.stubs.applyUnconfirmed)).is.true;
      expect(postStub.calledAfter(txLogic.stubs.apply)).is.true;
      expect(postStub.calledAfter(saveBlockStub)).is.true;
      expect(postStub.calledAfter(roundsModule.stubs.tick)).is.true;

    });
Example #7
0
    it('should use model result and modify original arr', async () => {
      modelFindAllStub.resolves([
        { transactionId: 2, username: '******'},
        { transactionId: 1, username: '******'},
      ]);
      const txs: any = [{id: 1}, {id: 2}];

      await instance.attachAssets(txs);

      expect(txs[0]).deep.eq({
        id: 1, asset: {
          delegate: {
            username: '******'
          },
        },
      });
      expect(txs[1]).deep.eq({
        id: 2, asset: {
          delegate: {
            username: '******'
          },
        },
      });
    });
    it('should use model result and modify original arr', async () => {
      modelFindAllStub.resolves([
        { transactionId: 2, publicKey: Buffer.from('bb', 'hex')},
        { transactionId: 1, publicKey: Buffer.from('aa', 'hex')},
      ]);
      const txs: any = [{id: 1}, {id: 2}];

      await instance.attachAssets(txs);

      expect(txs[0]).deep.eq({
        id: 1, asset: {
          signature: {
            publicKey: 'aa',
          },
        },
      });
      expect(txs[1]).deep.eq({
        id: 2, asset: {
          signature: {
            publicKey: 'bb',
          },
        },
      });
    });
module('JSONAPIRequestProcessor', function(hooks) {
  let fetchStub: SinonStub;
  let keyMap: KeyMap;
  let schema: Schema;
  let processor: JSONAPIRequestProcessor;

  hooks.beforeEach(() => {
    fetchStub = sinon.stub(self, 'fetch');
    schema = new Schema({
      models: {
        planet: {
          keys: {
            remoteId: {}
          },
          attributes: {
            name: { type: 'string' },
            classification: { type: 'string' },
            lengthOfDay: { type: 'number' }
          },
          relationships: {
            moons: { type: 'hasMany', model: 'moon', inverse: 'planet' },
            solarSystem: {
              type: 'hasOne',
              model: 'solarSystem',
              inverse: 'planets'
            }
          }
        },
        moon: {
          keys: {
            remoteId: {}
          },
          attributes: {
            name: { type: 'string' }
          },
          relationships: {
            planet: { type: 'hasOne', model: 'planet', inverse: 'moons' }
          }
        },
        solarSystem: {
          keys: {
            remoteId: {}
          },
          attributes: {
            name: { type: 'string' }
          },
          relationships: {
            planets: {
              type: 'hasMany',
              model: 'planet',
              inverse: 'solarSystem'
            }
          }
        }
      }
    });

    keyMap = new KeyMap();
    processor = new JSONAPIRequestProcessor({
      schema,
      keyMap,
      sourceName: 'foo'
    });
    processor.serializer.resourceKey = function() {
      return 'remoteId';
    };
  });

  hooks.afterEach(() => {
    keyMap = schema = processor = null;
    fetchStub.restore();
  });

  test('it exists', function(assert) {
    assert.ok(processor);
  });

  test('processor has default settings', function(assert) {
    assert.deepEqual(
      processor.allowedContentTypes,
      ['application/vnd.api+json', 'application/json'],
      'allowedContentTypes are set to default'
    );
  });

  test('#initFetchSettings will override defaults with custom settings provided', function(assert) {
    assert.deepEqual(
      processor.initFetchSettings({
        headers: {
          Accept: 'application/json'
        },
        method: 'POST',
        body: '{"data": {}}',
        timeout: 10000
      }),
      {
        headers: {
          Accept: 'application/json',
          'Content-Type': 'application/vnd.api+json'
        },
        method: 'POST',
        body: '{"data": {}}',
        timeout: 10000
      }
    );
  });

  test('#initFetchSettings will convert json to a stringified body', function(assert) {
    assert.deepEqual(
      processor.initFetchSettings({
        headers: {
          Accept: 'application/json'
        },
        method: 'POST',
        json: { data: { a: 123 } },
        timeout: 10000
      }),
      {
        headers: {
          Accept: 'application/json',
          'Content-Type': 'application/vnd.api+json'
        },
        method: 'POST',
        body: '{"data":{"a":123}}',
        timeout: 10000
      }
    );
  });

  test('#initFetchSettings will not include a `Content-Type` header with no body', function(assert) {
    assert.deepEqual(
      processor.initFetchSettings({
        method: 'GET'
      }),
      {
        headers: {
          Accept: 'application/vnd.api+json'
        },
        method: 'GET',
        timeout: 5000
      }
    );
  });

  test('#fetch - successful if one of the `allowedContentTypes` appears anywhere in `Content-Type` header', async function(assert) {
    assert.expect(6);
    fetchStub.withArgs('/planets/12345/relationships/moons').returns(
      jsonapiResponse(
        {
          status: 200,
          headers: {
            'Content-Type': 'application/vnd.api+json'
          }
        },
        { data: null }
      )
    );
    try {
      let result = await processor.fetch(
        '/planets/12345/relationships/moons',
        {}
      );
      assert.ok(
        result,
        'Accepts content that is _only_ the JSONAPI media type'
      );
    } catch (e) {
      assert.ok(
        false,
        'Should accept content that is _only_ the JSONAPI media type'
      );
    }

    fetchStub.withArgs('/planets/12345/relationships/moons').returns(
      jsonapiResponse(
        {
          status: 200,
          headers: {
            'Content-Type': 'multipart,application/vnd.api+json; charset=utf-8'
          }
        },
        { data: null }
      )
    );
    try {
      let result = await processor.fetch(
        '/planets/12345/relationships/moons',
        {}
      );
      assert.ok(result, 'Position of JSONAPI media type is not important');
    } catch (e) {
      assert.ok(false, 'Position of JSONAPI media type should not matter');
    }

    fetchStub.withArgs('/planets/12345/relationships/moons').returns(
      jsonapiResponse(
        {
          status: 200,
          headers: {
            'Content-Type': 'application/json'
          }
        },
        { data: null }
      )
    );
    try {
      let result = await processor.fetch(
        '/planets/12345/relationships/moons',
        {}
      );
      assert.ok(result, 'Processor will attempt to parse plain json');
    } catch (e) {
      assert.ok(false, 'Processor should parse plain json');
    }

    fetchStub.withArgs('/planets/12345/relationships/moons').returns(
      jsonapiResponse(
        {
          status: 200,
          headers: {
            'Content-Type': 'application/xml'
          }
        },
        {
          data: null
        }
      )
    );
    let result = await processor.fetch(
      '/planets/12345/relationships/moons',
      {}
    );
    assert.ok(
      result == null,
      'XML is not acceptable but HTTP Status is good, so just ignore response'
    );

    processor.allowedContentTypes = ['application/custom'];
    fetchStub.withArgs('/planets/12345/relationships/moons').returns(
      jsonapiResponse(
        {
          status: 200,
          headers: {
            'Content-Type': 'application/custom'
          }
        },
        {
          data: null
        }
      )
    );
    result = await processor.fetch('/planets/12345/relationships/moons', {});
    assert.ok(
      result,
      'will accept custom content type if specifically allowed'
    );

    fetchStub.withArgs('/planets/12345/relationships/moons').returns(
      jsonapiResponse({
        status: 204,
        headers: {
          'Content-Type': 'application/custom'
        }
      })
    );
    result = await processor.fetch('/planets/12345/relationships/moons', {});
    assert.ok(
      result === undefined,
      'A 204 - No Content response has no content'
    );
  });
});
  test('#fetch - successful if one of the `allowedContentTypes` appears anywhere in `Content-Type` header', async function(assert) {
    assert.expect(6);
    fetchStub.withArgs('/planets/12345/relationships/moons').returns(
      jsonapiResponse(
        {
          status: 200,
          headers: {
            'Content-Type': 'application/vnd.api+json'
          }
        },
        { data: null }
      )
    );
    try {
      let result = await processor.fetch(
        '/planets/12345/relationships/moons',
        {}
      );
      assert.ok(
        result,
        'Accepts content that is _only_ the JSONAPI media type'
      );
    } catch (e) {
      assert.ok(
        false,
        'Should accept content that is _only_ the JSONAPI media type'
      );
    }

    fetchStub.withArgs('/planets/12345/relationships/moons').returns(
      jsonapiResponse(
        {
          status: 200,
          headers: {
            'Content-Type': 'multipart,application/vnd.api+json; charset=utf-8'
          }
        },
        { data: null }
      )
    );
    try {
      let result = await processor.fetch(
        '/planets/12345/relationships/moons',
        {}
      );
      assert.ok(result, 'Position of JSONAPI media type is not important');
    } catch (e) {
      assert.ok(false, 'Position of JSONAPI media type should not matter');
    }

    fetchStub.withArgs('/planets/12345/relationships/moons').returns(
      jsonapiResponse(
        {
          status: 200,
          headers: {
            'Content-Type': 'application/json'
          }
        },
        { data: null }
      )
    );
    try {
      let result = await processor.fetch(
        '/planets/12345/relationships/moons',
        {}
      );
      assert.ok(result, 'Processor will attempt to parse plain json');
    } catch (e) {
      assert.ok(false, 'Processor should parse plain json');
    }

    fetchStub.withArgs('/planets/12345/relationships/moons').returns(
      jsonapiResponse(
        {
          status: 200,
          headers: {
            'Content-Type': 'application/xml'
          }
        },
        {
          data: null
        }
      )
    );
    let result = await processor.fetch(
      '/planets/12345/relationships/moons',
      {}
    );
    assert.ok(
      result == null,
      'XML is not acceptable but HTTP Status is good, so just ignore response'
    );

    processor.allowedContentTypes = ['application/custom'];
    fetchStub.withArgs('/planets/12345/relationships/moons').returns(
      jsonapiResponse(
        {
          status: 200,
          headers: {
            'Content-Type': 'application/custom'
          }
        },
        {
          data: null
        }
      )
    );
    result = await processor.fetch('/planets/12345/relationships/moons', {});
    assert.ok(
      result,
      'will accept custom content type if specifically allowed'
    );

    fetchStub.withArgs('/planets/12345/relationships/moons').returns(
      jsonapiResponse({
        status: 204,
        headers: {
          'Content-Type': 'application/custom'
        }
      })
    );
    result = await processor.fetch('/planets/12345/relationships/moons', {});
    assert.ok(
      result === undefined,
      'A 204 - No Content response has no content'
    );
  });