Skip to content

Network testing: Nicaea proposals- leader management and mint capacity#952

Merged
mnaamani merged 22 commits intoJoystream:nicaeafrom
gleb-urvanov:feature/manage-lead-proposals
Jul 20, 2020
Merged

Network testing: Nicaea proposals- leader management and mint capacity#952
mnaamani merged 22 commits intoJoystream:nicaeafrom
gleb-urvanov:feature/manage-lead-proposals

Conversation

@gleb-urvanov
Copy link
Contributor

implementation for following issues:
#683
#684
#685
#657

Depends on:
#782
#919

@gleb-urvanov gleb-urvanov added network-integration-test End-to-end full network integration test nicaea proposal-system labels Jul 15, 2020
@gleb-urvanov gleb-urvanov requested a review from mnaamani July 15, 2020 15:43
@gleb-urvanov gleb-urvanov marked this pull request as draft July 15, 2020 15:44
@gleb-urvanov gleb-urvanov marked this pull request as ready for review July 15, 2020 19:14
@mnaamani
Copy link
Member

can you merge nicaea to make this easier to review also

Copy link
Member

@mnaamani mnaamani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've left some comments and request for changes.
Overall the tests are running through till the end successfully.
I noticed that the nicaea tests don't cover the content working directory proposals. It is still part of nicaea runtime so they should not be left out. Please correct me if I'm wrong.

}

constructor() {}
constructor() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think passing in all values to the constructor would be a better pattern than depending on user to call each individueal set method. They may forget to set some values. Unless ofcourse you need to set the values at different times, in which case its probably not a good idea to bundle all the values in this single class.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Going to disagree with that point- one of the reasons why we have introduced a data transfer object is to mitigate the risk of passing the values in the wrong order: #782 (comment)

If all the values will be passed to the constructor, that goal will not be achieved- instead of risk having the wrong order in the function call, we will have the same risk but for the constructor call.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes as in the original comment you linked to I wasn't implying re-introducing a long list of arguments. But this approach here of depending on methods might leave some of the private fields un-initialized?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it might. Normally typescript won't allow that, that is why assignment assertion (exclamation sign) is used. What is the best approach you can suggest?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes so to this point exactly, I guess the approach here mainly solves problem of passing arguments in wrong order.
So passing an object was the correct solution, except now this new object can have null or underfined fields. So wouldn't simply passing arguments like so be the optimal approach:

// original approach which I advised against
function withHugeArgsList(a, b, c, ...., z) {

}

// suggested method, takes a single object instead
function theMethod({ arg_a: A, arg_b: B, arg_c: C }) {
   do_something_with_args()
}

// calling the method
theMethod({arg_a: valueOfTypeA, arg_b: valueOfTypeB, ...})

This way will insure no incorrect ordering of arguments, and that all args are provided (optionally give some args a default value if you want them to be optional)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, will do.


public expectLeaderTerminated(): Promise<void> {
return new Promise(async resolve => {
await this.api.query.system.events<Vec<EventRecord>>(events => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in all these expectSomeEvent() methods, there is no need for async/await the way it is written, since we are relying on callback and then calling resolve(). Infact I think the api.query.system.events() method returns a promise for a function to unsubscribe from listening to events, which we should probably use before resolve(). As promises work it will only ever resolve to the first event found, but there is unnecessary events still being processed up until the api disconnects.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in #1001 , although it is worth to look at the solution. With unsubscribe introduction we have to await for unsubscription promise, thus the await is gaining sense. Please correct me if im wrong.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well yes if you are actually going to use it, then you need the await :)
And I see you did refactor it in #1001 as you mentioned. 👍

import BN from 'bn.js';

export class FillOpeningParameters {
private amountPerPayout!: BN;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my typescript knowledge is a bit rusty, does the ! mean required ? If so isn't it a problem that you can construct the class without giving all these fields a value at construction time?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With ! we asserting the value will be assigned before it will be used. As alternative, we could initialize the values with default values, but the object is not intended to be used without all fields being initialized.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok thanks for clarification. So we are infact using the ! incorrectly, the fields should then be initialized in the constructor. No ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With initialization in the constructor, the assignment assertion will be excessive (since the valued are guaranteed to be initialized). So, when the values will be initialized in the constructor, ! will be removed.


let createOpeningProposalId: BN;
let openingId: BN;
tap.test(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see now the mechanism you mentioned at one point for getting results out of a test. It works well and I suppose its the only way really. But it can be improved. I think we need to do a better approach of encapsulating the calls, context and results of executing them to make things cleaner.

What would be really nice to see is something like this:

const context = { apiWrapper, keys, ... }
const params = { applicationStake, roleStake, ... } 
const expectations = { expectFailure, expectedValue/Events }

// asserts that state of runtime is what is expected for execution and prepares the execution to run
// in a tap.test call
const createOpeningFixture = new CreateWorkingGroupLeaderOpeningFixture(context, params)

// runs and asserts on expectations
tap.test('opening', createOpeningFixture.runner(expectations))

// at this point the createOpeningFixture has within it the result of execution, events, the porposalId for example.
// that can be used as inputs for the following tests.
createOpeningFixture.result 
createOpeningFixture.events
createOpeningFixture.openingId 

This is what I would have really liked to see as a pattern throughout. This makes constructing and writing scenarios very reusable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made a note for a future refactoring in Iznik

@mnaamani
Copy link
Member

I'm going to merge this now to have a the tests in the nicaea branch, but please take a note of the suggested refactoring in the next iteration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

network-integration-test End-to-end full network integration test nicaea proposal-system

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants