github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/kat/src/test/ethereum/assets/authored/private/described-structured.ts (about) 1 // Copyright © 2021 Kaleido, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 import assert from 'assert'; 16 import { createHash, randomBytes } from 'crypto'; 17 import nock from 'nock'; 18 import request from 'supertest'; 19 import { promisify } from 'util'; 20 import { IDBAssetDefinition, IDBAssetInstance, IEventAssetDefinitionCreated, IEventAssetInstanceBatchCreated } from '../../../../../lib/interfaces'; 21 import * as utils from '../../../../../lib/utils'; 22 import { app, mockEventStreamWebSocket } from '../../../../common'; 23 import { testContent, testDescription, testIndexes } from '../../../../samples'; 24 const delay = promisify(setTimeout); 25 26 export const testAuthoredPrivateDescribedStructured = () => { 27 describe('Assets: authored - private - described - structured', async () => { 28 29 let assetDefinitionID: string; 30 const assetDefinitionName = 'authored - private - described - structured'; 31 const timestamp = utils.getTimestamp(); 32 const batchHashSha256 = '0x' + createHash('sha256').update(randomBytes(10)).digest().toString('hex'); 33 const batchHashIPFSMulti = utils.sha256ToIPFSHash(batchHashSha256); 34 35 let batchMaxRecordsToRestore: number; 36 beforeEach(() => { 37 nock.cleanAll(); 38 // Force batches to close immediately 39 batchMaxRecordsToRestore = utils.constants.BATCH_MAX_RECORDS; 40 utils.constants.BATCH_MAX_RECORDS = 1; 41 }); 42 43 afterEach(() => { 44 assert.deepStrictEqual(nock.pendingMocks(), []); 45 utils.constants.BATCH_MAX_RECORDS = batchMaxRecordsToRestore; 46 }); 47 48 describe('Create asset definition', () => { 49 50 it('Checks that the asset definition can be added', async () => { 51 52 nock('https://apigateway.kaleido.io') 53 .post('/createAssetDefinition?kld-from=0x0000000000000000000000000000000000000001&kld-sync=false') 54 .reply(200, { id: 'my-receipt-id' }); 55 56 nock('https://ipfs.kaleido.io') 57 .post('/api/v0/add') 58 .reply(200, { Hash: 'QmQX1g8GwrMuACuMfQmKXzeYd7yXMXPpcUaFMqLUzSv1yL' }); 59 60 const result = await request(app) 61 .post('/api/v1/assets/definitions') 62 .send({ 63 name: assetDefinitionName, 64 author: '0x0000000000000000000000000000000000000001', 65 isContentPrivate: true, 66 isContentUnique: true, 67 descriptionSchema: testDescription.schema.object, 68 contentSchema: testContent.schema.object, 69 indexes: testIndexes 70 }) 71 .expect(200); 72 assert.deepStrictEqual(result.body.status, 'submitted'); 73 assetDefinitionID = result.body.assetDefinitionID; 74 75 const getAssetDefinitionsResponse = await request(app) 76 .get('/api/v1/assets/definitions') 77 .expect(200); 78 const assetDefinition = getAssetDefinitionsResponse.body.find((assetDefinition: IDBAssetDefinition) => assetDefinition.name === 'authored - private - described - structured'); 79 assert.strictEqual(assetDefinition.assetDefinitionID, assetDefinitionID); 80 assert.strictEqual(assetDefinition.author, '0x0000000000000000000000000000000000000001'); 81 assert.strictEqual(assetDefinition.isContentPrivate, true); 82 assert.strictEqual(assetDefinition.isContentUnique, true); 83 assert.deepStrictEqual(assetDefinition.descriptionSchema, testDescription.schema.object); 84 assert.deepStrictEqual(assetDefinition.contentSchema, testContent.schema.object); 85 assert.strictEqual(assetDefinition.name, 'authored - private - described - structured'); 86 assert.strictEqual(assetDefinition.receipt, 'my-receipt-id'); 87 assert.strictEqual(typeof assetDefinition.submitted, 'number'); 88 89 const getAssetDefinitionResponse = await request(app) 90 .get(`/api/v1/assets/definitions/${assetDefinitionID}`) 91 .expect(200); 92 assert.deepStrictEqual(assetDefinition, getAssetDefinitionResponse.body); 93 }); 94 95 it('Checks that the event stream notification for confirming the asset definition creation is handled', async () => { 96 const eventPromise = new Promise<void>((resolve) => { 97 mockEventStreamWebSocket.once('send', message => { 98 assert.strictEqual(message, '{"type":"ack","topic":"dev"}'); 99 resolve(); 100 }) 101 }); 102 103 nock('https://ipfs.kaleido.io') 104 .get('/ipfs/QmQX1g8GwrMuACuMfQmKXzeYd7yXMXPpcUaFMqLUzSv1yL') 105 .reply(200, { 106 assetDefinitionID: assetDefinitionID, 107 name: assetDefinitionName, 108 isContentPrivate: true, 109 isContentUnique: true, 110 descriptionSchema: testDescription.schema.object, 111 contentSchema: testContent.schema.object 112 }); 113 114 const data: IEventAssetDefinitionCreated = { 115 author: '0x0000000000000000000000000000000000000001', 116 assetDefinitionHash: '0x205ee35b47f713845ea616c805e346cb90e9de82e56069f0de318c59e57d867b', 117 timestamp: timestamp.toString() 118 }; 119 mockEventStreamWebSocket.emit('message', JSON.stringify([{ 120 signature: utils.contractEventSignatures.ASSET_DEFINITION_CREATED, 121 data, 122 blockNumber: '123', 123 transactionHash: '0x0000000000000000000000000000000000000000000000000000000000000000' 124 }])); 125 await eventPromise; 126 }); 127 128 it('Checks that the asset definition is confirmed', async () => { 129 const getAssetDefinitionsResponse = await request(app) 130 .get('/api/v1/assets/definitions') 131 .expect(200); 132 const assetDefinition = getAssetDefinitionsResponse.body.find((assetDefinition: IDBAssetDefinition) => assetDefinition.name === 'authored - private - described - structured'); 133 assert.strictEqual(assetDefinition.assetDefinitionID, assetDefinitionID); 134 assert.strictEqual(assetDefinition.author, '0x0000000000000000000000000000000000000001'); 135 assert.strictEqual(assetDefinition.isContentPrivate, true); 136 assert.strictEqual(assetDefinition.isContentUnique, true); 137 assert.deepStrictEqual(assetDefinition.descriptionSchema, testDescription.schema.object); 138 assert.deepStrictEqual(assetDefinition.contentSchema, testContent.schema.object); 139 assert.strictEqual(typeof assetDefinition.submitted, 'number'); 140 assert.strictEqual(assetDefinition.name, 'authored - private - described - structured'); 141 assert.strictEqual(assetDefinition.timestamp, timestamp); 142 assert.strictEqual(assetDefinition.receipt, 'my-receipt-id'); 143 assert.strictEqual(assetDefinition.blockNumber, 123); 144 assert.strictEqual(assetDefinition.transactionHash, '0x0000000000000000000000000000000000000000000000000000000000000000'); 145 146 const getAssetDefinitionResponse = await request(app) 147 .get(`/api/v1/assets/definitions/${assetDefinitionID}`) 148 .expect(200); 149 assert.deepStrictEqual(assetDefinition, getAssetDefinitionResponse.body); 150 }); 151 152 }); 153 154 describe('Asset instances', async () => { 155 156 let assetInstanceID: string; 157 158 it('Checks that an asset instance can be created, within a batch', async () => { 159 160 nock('https://apigateway.kaleido.io') 161 .post('/createAssetInstanceBatch?kld-from=0x0000000000000000000000000000000000000001&kld-sync=false') 162 .reply(200, { id: 'my-receipt-id' }); 163 164 nock('https://ipfs.kaleido.io') 165 .post('/api/v0/add') 166 .reply(200, { Hash: batchHashIPFSMulti }) 167 168 const result = await request(app) 169 .post(`/api/v1/assets/${assetDefinitionID}`) 170 .send({ 171 author: '0x0000000000000000000000000000000000000001', 172 description: testDescription.sample.object, 173 content: testContent.sample.object 174 }) 175 .expect(200); 176 assert.deepStrictEqual(result.body.status, 'submitted'); 177 assetInstanceID = result.body.assetInstanceID; 178 179 const getAssetInstancesResponse = await request(app) 180 .get(`/api/v1/assets/${assetDefinitionID}`) 181 .expect(200); 182 const assetInstance = getAssetInstancesResponse.body.find((assetInstance: IDBAssetInstance) => assetInstance.assetInstanceID === assetInstanceID); 183 assert.strictEqual(assetInstance.author, '0x0000000000000000000000000000000000000001'); 184 assert.strictEqual(assetInstance.assetDefinitionID, assetDefinitionID); 185 assert.deepStrictEqual(assetInstance.description, testDescription.sample.object); 186 assert.deepStrictEqual(assetInstance.content, testContent.sample.object); 187 assert.strictEqual(typeof assetInstance.submitted, 'number'); 188 assert.strictEqual(typeof assetInstance.batchID, 'string'); 189 190 // Expect the batch to have been submitted 191 let getBatchResponse: any; 192 for (let i = 0; i < 10; i++) { 193 getBatchResponse = await request(app) 194 .get(`/api/v1/batches/${assetInstance.batchID}`) 195 .expect(200); 196 if (getBatchResponse.body.completed) break; 197 await delay(1); 198 } 199 assert.strictEqual(typeof getBatchResponse.body.completed, 'number'); 200 assert.strictEqual(typeof getBatchResponse.body.batchHash, 'string'); 201 assert.strictEqual(getBatchResponse.body.receipt, 'my-receipt-id'); 202 assert.strictEqual(getBatchResponse.body.batchHash, batchHashSha256); 203 // As this is a private asset, the content must not have been written to IPFS in the batch 204 assert.strictEqual(getBatchResponse.body.records[0].content, undefined); 205 // The full description payload should be in the batch data written to IPFS in the batch 206 assert.deepStrictEqual(getBatchResponse.body.records[0].description, testDescription.sample.object); 207 208 const getAssetInstanceResponse = await request(app) 209 .get(`/api/v1/assets/${assetDefinitionID}/${assetInstanceID}`) 210 .expect(200); 211 assert.deepStrictEqual(assetInstance, getAssetInstanceResponse.body); 212 213 }); 214 215 it('Checks that the event stream notification for confirming the asset instance creation is handled', async () => { 216 const eventPromise = new Promise<void>((resolve) => { 217 mockEventStreamWebSocket.once('send', message => { 218 assert.strictEqual(message, '{"type":"ack","topic":"dev"}'); 219 resolve(); 220 }) 221 }); 222 const data: IEventAssetInstanceBatchCreated = { 223 author: '0x0000000000000000000000000000000000000001', 224 batchHash: batchHashSha256, 225 timestamp: timestamp.toString() 226 }; 227 mockEventStreamWebSocket.emit('message', JSON.stringify([{ 228 signature: utils.contractEventSignatures.ASSET_INSTANCE_BATCH_CREATED, 229 data, 230 blockNumber: '123', 231 transactionHash: '0x0000000000000000000000000000000000000000000000000000000000000000' 232 }])); 233 await eventPromise; 234 }); 235 236 it('Checks that the asset instance is confirmed', async () => { 237 const getAssetInstancesResponse = await request(app) 238 .get(`/api/v1/assets/${assetDefinitionID}`) 239 .expect(200); 240 const assetInstance = getAssetInstancesResponse.body.find((assetInstance: IDBAssetInstance) => assetInstance.assetInstanceID === assetInstanceID); 241 assert.strictEqual(assetInstance.author, '0x0000000000000000000000000000000000000001'); 242 assert.strictEqual(assetInstance.assetDefinitionID, assetDefinitionID); 243 assert.deepStrictEqual(assetInstance.description, testDescription.sample.object); 244 assert.deepStrictEqual(assetInstance.content, testContent.sample.object); 245 assert.strictEqual(assetInstance.timestamp, timestamp); 246 assert.strictEqual(typeof assetInstance.submitted, 'number'); 247 assert.strictEqual(assetInstance.receipt, undefined); // the batch has the receipt 248 assert.strictEqual(assetInstance.blockNumber, 123); 249 assert.strictEqual(assetInstance.transactionHash, '0x0000000000000000000000000000000000000000000000000000000000000000'); 250 251 const getAssetInstanceResponse = await request(app) 252 .get(`/api/v1/assets/${assetDefinitionID}/${assetInstanceID}`) 253 .expect(200); 254 assert.deepStrictEqual(assetInstance, getAssetInstanceResponse.body); 255 }); 256 257 it('sets a property on an asset, which will be public and batched even though the asset is private', async () => { 258 259 nock('https://apigateway.kaleido.io') 260 .post('/createAssetInstanceBatch?kld-from=0x0000000000000000000000000000000000000001&kld-sync=false') 261 .reply(200, { id: 'my-receipt-id' }); 262 263 nock('https://ipfs.kaleido.io') 264 .post('/api/v0/add') 265 .reply(200, { Hash: batchHashIPFSMulti }) 266 267 const {body: result} = await request(app) 268 .put(`/api/v1/assets/${assetDefinitionID}/${assetInstanceID}`) 269 .send({ 270 action: 'set-property', 271 key: 'key', 272 value: 'value', 273 author: '0x0000000000000000000000000000000000000001', 274 }) 275 .expect(200); 276 assert.deepStrictEqual(result.status, 'submitted'); 277 278 const {body: asset} = await request(app) 279 .get(`/api/v1/assets/${assetDefinitionID}/${assetInstanceID}`) 280 .expect(200); 281 const prop = asset.properties['0x0000000000000000000000000000000000000001']['key']; 282 assert.strictEqual(prop.value, 'value'); 283 assert(prop.submitted > 0); 284 const {batchID} = prop; 285 286 // Expect the batch to have been submitted 287 let getBatchResponse: any; 288 for (let i = 0; i < 10; i++) { 289 getBatchResponse = await request(app) 290 .get(`/api/v1/batches/${batchID}`) 291 .expect(200); 292 if (getBatchResponse.body.completed) break; 293 await delay(1); 294 } 295 296 assert.strictEqual(typeof getBatchResponse.body.completed, 'number'); 297 assert.strictEqual(typeof getBatchResponse.body.batchHash, 'string'); 298 assert.strictEqual(getBatchResponse.body.receipt, 'my-receipt-id'); 299 assert.strictEqual(getBatchResponse.body.batchHash, batchHashSha256); 300 // As properties are always public, the full content will have been written to IPFS in the batch 301 assert.deepStrictEqual(getBatchResponse.body.records[0].recordType, 'property'); 302 assert.deepStrictEqual(getBatchResponse.body.records[0].key, 'key'); 303 assert.deepStrictEqual(getBatchResponse.body.records[0].value, 'value'); 304 305 }); 306 307 }); 308 309 }); 310 311 };