Diego Barahona

CTO & Architect @ Covalent

Convector Unit-Test Identities

This blog post is based on the content of episode CDOH #12If you want to get a more in depth explanation, checkout the episode.
Mock user generation
Testing your smart contracts has been always a top priority for the Convector Core development team, and because of that, this week we released some interesting features improving the capabilities for managing the identities for unit-testing.
Starting with Convector v1.3.6 the MockControllerAdapter will generate certificates for you, so you can use multiple identities to emulate correctly the identities pattern for example. Identity is a critical part of every chaincode and thus it needs to be very well tested and make sure your business logic is the one you expect it to be.
// Create the mock controller adapter
const adapter = new MockControllerAdapter();
// Create the participant client
const participantCtrl = ClientFactory(ParticipantController, adapter);

// Initialize the adapter with all the controllers
await adapter.init([...]);

// Register an user
adapter.addUser('MyUser1');

// Create the participant model
const participantModel = new Participant({
  id: uuid(),
  name: 'Diego'
});

// Invoke a function using the corresponding username
await participantCtrl.$withUser('MyUser1').register(participantModel);

// Retrieve the participant from network
const savedModel = await Participant.getOne(participantModel.id);

// Compare the identity assigned in the controller using `this.sender`...
expect(savedModel.identity)
  // ... with the fingerprint of the mocked user
  .to.eq(adapter.getUserFingerprint('MyUser1'))
Two new methods in the MockControllerAdapter,
addUser('username')
which will generate the credentials for the mock engine to use a custom certificate. This will cause that
this.sender
to be different in the controllers, it will now be the fingerprint of the fake user. If you call
addUser('username', 'certificate')
it will then use the custom certificate you provide, this may be useful if you want to use an existing identity with attributes on it.
There's also
adapter.getUserFingerprint('username')
which returns the fingerprint for the specified user, previously registered. This is useful for when you want to make assertions about the assigned identity in a model, of if you're using fingerprints for parameters.
The helper
$withUser('username')
is available in the ControllerClient. You can know more about it in the episode CDOH #10
End-to-end test by default
The CLI v1.1.6 now creates the unit-test files using a fake user with the methods shown above. It will also create E2E tests by default for each chaincode. You can use these tests to test agains Hurley. So in the tests folder you'll find a controller.spec.ts file, but now also a controller.e2e.ts file, using a FabricControllerAdapter, instead of a MockControllerAdapter. This will be configured to send the transactions to Hurley, but you can change the target to whatever Fabric Network you have.
You can also have the ability to change the user with
$withUser('user1')
and it will use the user1 from the current organization specified in the adapter for current client. You can create multiple FabricControllerAdapters, one for each organization, and change the adapter used for each client doing
controllerCient.adapter = adapterForCovalent
Network configuration file
On Hurley v1.1.0 we now have the ability to start the network based on a configuration file. This is for advanced use cases of Hurley where you need to change the channel name, the user names, or the organization names. Create a JSON file at the root of your project and pass it as an argument to Hurley with
hurl new -n hurley.json
{
  "channels": ["public", "private"],
  "topology": {
    "hyperledger": {
      "channels": ["public"],
      "users": ["john", "mike"]
    },
    "covalent": {
      "channels": ["public", "private"],
      "users": ["diego"]
    },
    "linux": {
      "channels": ["public", "private"],
      "users": ["henry"]
    }
  }
}
If you do this, you'll need to specify the channel
-C public
and one of the organizations
-o covalent
when installing, upgrading, and invoking the chaincode.
Special thanks to J3SR0 for contributing the configuration file changes for Hurley. We really appreciate that.
Quick tip: how to get a fingerprint of any given user
{
  ...,
  "scripts": {
    ...,
    "user:fingerprint": "f () {  node -e \"const homedir = require('os').homedir(); console.log(JSON.parse(require('fs').readFileSync('/'+homedir+'/hyperledger-fabric-network/.hfc-$2/$1', 'utf8')).enrollment.identity.certificate)\" | openssl x509 -fingerprint -noout | cut -d '=' -f2 ; }; f",
  }
}
Copy a function like that into your package.json file, and you'll be able to run it with something like
npm run user:fingerprint -- user1 org1
to read one of Hurley users.
We hope convector helps you design really valuable code for your applications. Our objective is for the developer to have all the necessary tools to create bug-free smart contracts or to reduce the risk to the minimum.
If you're working with Convector, join the community and let us know what you're building with it. We will be happy to assist you if you're having any problems.

Tags

Comments

More by Diego Barahona

Topics of interest