You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ssb-did-resolver/gen-test.js

258 lines
8.4 KiB

// did:ssb test vector generator for DID Test Suite
const DIDSSBResolver = require('./resolver');
const dest = process.argv[2];
if (!dest) throw 'Usage: node gen-test.js <destination>';
const path = require('path');
const fs = require('fs');
const didJsonFilename = path.join(dest, 'did-ssb.json');
const resolverJsonFilename = path.join(dest, 'resolver-ssb.json');
const dereferencerJsonFilename = path.join(dest, 'dereferencer-ssb.json');
const testCommon = {
didMethod: 'did:ssb',
implementation: 'ssb-did-resolver',
implementer: 'Secure Scuttlebutt Consortium',
};
const didMethodInputs = [
{
did: 'did:ssb:ed25519:f_6sQ6d2CMxRUhLpspgGIulDxDCwYD7DzFzPNr7u5AU',
resolutionOptions: {},
representationResolutionOptions: {}
}
];
const resolverInputs = [
{
function: 'resolve',
did: 'did:ssb:ed25519:f_6sQ6d2CMxRUhLpspgGIulDxDCwYD7DzFzPNr7u5AU',
resolutionOptions: {}
},
{
function: 'resolveRepresentation',
did: 'did:ssb:ed25519:f_6sQ6d2CMxRUhLpspgGIulDxDCwYD7DzFzPNr7u5AU',
resolutionOptions: {}
},
{
function: 'resolve',
did: 'did:ssb:ed25519:6RpN4Ztw3jLwzQtHl8XpnnR58LWZTAjwq2vvfyx7zkc',
resolutionOptions: {}
}
];
const dereferencerInputs = [
{
didUrl: 'did:ssb:ed25519:f_6sQ6d2CMxRUhLpspgGIulDxDCwYD7DzFzPNr7u5AU',
dereferenceOptions: {}
},
{
didUrl: 'did:ssb:ed25519:f_6sQ6d2CMxRUhLpspgGIulDxDCwYD7DzFzPNr7u5AU?versionId=%25Vlo6kAc%2BIbGGBhD2MUi2r3ULz%2FNAGBWwGb%2FEMa4w4FI%3D.sha256',
dereferenceOptions: {}
},
{
didUrl: 'did:ssb:ed25519:f_6sQ6d2CMxRUhLpspgGIulDxDCwYD7DzFzPNr7u5AU?versionTime=2021-07-24T03:38:45Z',
dereferenceOptions: {}
}
];
let didParameters = {
versionId: 'did:ssb:ed25519:f_6sQ6d2CMxRUhLpspgGIulDxDCwYD7DzFzPNr7u5AU?versionId=%25Vlo6kAc%2BIbGGBhD2MUi2r3ULz%2FNAGBWwGb%2FEMa4w4FI%3D.sha256',
versionTime: 'did:ssb:ed25519:f_6sQ6d2CMxRUhLpspgGIulDxDCwYD7DzFzPNr7u5AU?versionTime=2021-07-24T03:38:46Z'
};
const ssbClient = require('ssb-client');
let closeSbot, resolver;
ssbClient(function (err, sbot, config) {
if (err) throw err;
closeSbot = sbot.close.bind(sbot);
resolver = new DIDSSBResolver(sbot, config);
generateReports(done)
});
function done() {
closeSbot(function (err) {
if (err && err !== true) throw err;
});
}
function generateReports(next) {
let reportWaiting = 3;
generateMethodReport(reportNext);
generateResolverReport(reportNext);
generateDereferencerReport(reportNext);
function reportNext() {
if (--reportWaiting) return;
next();
}
}
function generateMethodReport(next) {
let contentTypes = {};
let supportedContentTypes = [];
let dids = [];
let didMethodReport = {
...testCommon,
supportedContentTypes,
dids,
didParameters,
};
let methodWaiting = 0;
didMethodInputs.forEach(function (input) {
methodWaiting++;
const did = input.did;
const didReport = didMethodReport[did] = {};
dids.push(did);
let didWaiting = 2;
let resResult, resReprResult;
resolver.resolve(did, input.resolutionOptions, function (didResolutionMetadata, didDocument, didDocumentMetadata) {
if (didResolutionMetadata.error) throw didResolutionMetadata.error;
resResult = {didResolutionMetadata, didDocument, didDocumentMetadata};
didNext();
});
resolver.resolveRepresentation(did, input.representationResolutionOptions, function (didResolutionMetadata, didDocumentStream, didDocumentMetadata) {
if (didResolutionMetadata.error) throw didResolutionMetadata.error;
resReprResult = {didResolutionMetadata, didDocumentStream, didDocumentMetadata};
didNext();
});
function didNext() {
if (--didWaiting) return;
const {didDocument} = resResult;
let representationSpecificEntries = {};
let properties = {};
for (const property in didDocument) switch (property) {
case '@context':
representationSpecificEntries[property] = didDocument[property];
break;
default:
properties[property] = didDocument[property];
break;
}
didReport.didDocumentDataModel = {
properties
};
const contentType = resReprResult.didResolutionMetadata.contentType;
if (!contentType) throw new Error('Expected contentType');
if (!contentTypes[contentType]) {
contentTypes[contentType] = true
supportedContentTypes.push(contentType);
}
didReport[contentType] = {
didDocumentDataModel: {
representationSpecificEntries
},
representation: resReprResult.didDocumentStream,
didDocumentMetadata: resReprResult.didDocumentMetadata,
didResolutionMetadata: resReprResult.didResolutionMetadata
};
methodNext();
}
});
function methodNext() {
if (--methodWaiting) return;
const report = JSON.stringify(didMethodReport, null, 2);
fs.writeFileSync(didJsonFilename, report);
next();
}
}
function getOutcome(error, deactivated) {
if (error) switch (error) {
case 'invalidDid': return 'invalidDidErrorOutcome';
case 'invalidDidUrl': return 'invalidDidUrlErrorOutcome';
case 'notFound': return 'notFoundErrorOutcome';
case 'representationNotSupported': return 'representationNotSupportedErrorOutcome';
default: throw new Error('Unknown error: ' + error);
}
if (deactivated) return 'deactivatedOutcome';
return 'defaultOutcome';
}
function generateResolverReport(next) {
let expectedOutcomes = {};
let executions = [];
const didResolverReport = {
...testCommon,
expectedOutcomes,
executions
};
let executionWaiting = resolverInputs.length;
resolverInputs.forEach(function (input, i) {
switch (input.function) {
case 'resolve':
resolver.resolve(input.did, input.resolutionOptions, function (didResolutionMetadata, didDocument, didDocumentMetadata) {
executions[i] = {
function: input.function,
input: {did: input.did, resolutionOptions: input.resolutionOptions},
output: {didResolutionMetadata, didDocument, didDocumentMetadata}
};
const outcome = getOutcome(didResolutionMetadata.error, didDocumentMetadata.deactivated);
const idxs = expectedOutcomes[outcome] || (expectedOutcomes[outcome] = []);
idxs.push(i);
executionNext();
});
break;
case 'resolveRepresentation':
resolver.resolveRepresentation(input.did, input.resolutionOptions, function (didResolutionMetadata, didDocumentStream, didDocumentMetadata) {
executions[i] = {
function: input.function,
input: {did: input.did, resolutionOptions: input.resolutionOptions},
output: {didResolutionMetadata, didDocumentStream, didDocumentMetadata}
};
const outcome = getOutcome(didResolutionMetadata.error, didDocumentMetadata.deactivated);
const idxs = expectedOutcomes[outcome] || (expectedOutcomes[outcome] = []);
idxs.push(i);
executionNext();
});
break;
default:
throw new Error('Unknown function: ' + input.function);
}
});
function executionNext() {
if (--executionWaiting) return;
for (const outcome in expectedOutcomes) {
expectedOutcomes[outcome].sort();
}
const report = JSON.stringify(didResolverReport, null, 2);
fs.writeFileSync(resolverJsonFilename, report);
next();
}
}
function generateDereferencerReport(next) {
let expectedOutcomes = {};
let executions = [];
const didUrlDereferencerReport = {
...testCommon,
expectedOutcomes,
executions
};
let executionWaiting = dereferencerInputs.length;
dereferencerInputs.forEach(function (input, i) {
resolver.dereference(input.didUrl, input.dereferenceOptions, function (dereferencingMetadata, contentStream, contentMetadata) {
executions[i] = {
function: 'dereference',
input,
output: {dereferencingMetadata, contentStream, contentMetadata}
};
const outcome = getOutcome(dereferencingMetadata.error, contentMetadata.deactivated);
const idxs = expectedOutcomes[outcome] || (expectedOutcomes[outcome] = []);
idxs.push(i);
executionNext();
});
});
function executionNext() {
if (--executionWaiting) return;
for (const outcome in expectedOutcomes) {
expectedOutcomes[outcome].sort();
}
const report = JSON.stringify(didUrlDereferencerReport, null, 2);
fs.writeFileSync(dereferencerJsonFilename, report);
next();
}
}