diff --git a/lib/contract.ts b/lib/contract.ts index 244104a..6d86b4c 100644 --- a/lib/contract.ts +++ b/lib/contract.ts @@ -648,8 +648,8 @@ export default class Contract { // the list of hashes we should check against. const match = matches(omit(matcher.raw.data, ['slug', 'version'])); const versionMatch = matcher.raw.data.version; - if (contract.raw.capabilities) { - for (const capability of contract.raw.capabilities) { + if (contract.raw.provides) { + for (const capability of contract.raw.provides) { if (match(capability)) { if (versionMatch) { if (valid(capability.version) && validRange(versionMatch)) { diff --git a/lib/index.ts b/lib/index.ts index 18f3bf1..4922ced 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -13,6 +13,7 @@ import { BlueprintLayout, BlueprintObject, ContractObject } from './types'; import Contract from './contract'; import Blueprint from './blueprint'; import Universe from './universe'; +import ObjectSet from './object-set'; import { buildTemplate } from './partials'; export { @@ -22,6 +23,10 @@ export { Contract, Blueprint, Universe, + // this is exported as is one of the return types of + // Contract.getNotSatisfiedChildRequirements + // TODO: remove this comment once the library has correct typings + ObjectSet, buildTemplate, }; diff --git a/tests/contract/satisfies-child-contract.spec.ts b/tests/contract/satisfies-child-contract.spec.ts index 839b3c4..c0b00aa 100644 --- a/tests/contract/satisfies-child-contract.spec.ts +++ b/tests/contract/satisfies-child-contract.spec.ts @@ -769,6 +769,160 @@ describe('Contract satisfiesChildContract', () => { ).to.be.true; }); + it('should return true given two fulfilled requirements from a context declaring capabilities with `provides`', () => { + const container = new Contract({ + type: 'foo', + slug: 'bar', + }); + + const contract1 = new Contract({ + type: 'meta.context', + slug: 'test', + provides: [ + { + type: 'sw.os', + slug: 'debian', + version: 'wheezy', + }, + { + type: 'arch.sw', + slug: 'amd64', + version: '1', + }, + ], + }); + + container.addChild(contract1); + + expect( + container.satisfiesChildContract( + new Contract({ + name: 'Node.js', + slug: 'nodejs', + type: 'sw.stack', + requires: [ + { + slug: 'debian', + type: 'sw.os', + }, + { + or: [ + { + slug: 'amd64', + type: 'arch.sw', + }, + { + slug: 'i386', + type: 'arch.sw', + }, + ], + }, + ], + }), + ), + ).to.be.true; + }); + + it('should return true given one fulfilled requirements from a context declaring capabilities with `provides` and one selected type', () => { + const container = new Contract({ + type: 'foo', + slug: 'bar', + }); + + const contract1 = new Contract({ + type: 'meta.context', + slug: 'test', + provides: [ + { + type: 'sw.os', + slug: 'debian', + version: 'wheezy', + }, + ], + }); + + container.addChild(contract1); + + expect( + container.satisfiesChildContract( + new Contract({ + name: 'Node.js', + slug: 'nodejs', + type: 'sw.stack', + requires: [ + { + slug: 'debian', + type: 'sw.os', + }, + { + or: [ + { + slug: 'amd64', + type: 'arch.sw', + }, + { + slug: 'i386', + type: 'arch.sw', + }, + ], + }, + ], + }), + { types: new Set(['sw.os']) }, + ), + ).to.be.true; + }); + + it('should return false given only one fulfilled requirements from a context declaring capabilities with `provides`', () => { + const container = new Contract({ + type: 'foo', + slug: 'bar', + }); + + const contract1 = new Contract({ + type: 'meta.context', + slug: 'test', + provides: [ + { + type: 'sw.os', + slug: 'debian', + version: 'wheezy', + }, + ], + }); + + container.addChild(contract1); + + const child = new Contract({ + name: 'Node.js', + slug: 'nodejs', + type: 'sw.stack', + requires: [ + { + slug: 'debian', + type: 'sw.os', + }, + { + or: [ + { + slug: 'amd64', + type: 'arch.sw', + }, + { + slug: 'i386', + type: 'arch.sw', + }, + ], + }, + ], + }); + + expect(container.satisfiesChildContract(child)).to.be.false; + expect(container.getNotSatisfiedChildRequirements(child)).to.have.lengthOf( + 1, + ); + }); + it('should return false given one unfulfilled requirement from a context with a composite contract', () => { const container = new Contract({ type: 'foo',