-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Mock resource monitor #3738
Mock resource monitor #3738
Conversation
Usage examples for Node and Python: https://gist.github.com/pgavlin/8904a129d1c5e826606ca8482b6386bd |
@mikhailshilkov / @CyrusNajmabadi could one of you take a stab at implementing this for .NET? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great! Longer term, I wonder if the schema work could help us generate and publish mock resources: https://gist.github.com/pgavlin/8904a129d1c5e826606ca8482b6386bd#file-ec2tests-js-L8-L17
.NET work is in #3696 |
I'd love to see an example in TypeScript to see how much type safety is available. |
I'll be trying this out in TypeScript (via ts-jest) today. We currently use mocha+sinon to accomplish a lot of what this PR does. Pain points have been:
Will report back on our testing experience using this branch. cc @joeduffy |
Can someone give me a hand with installing this branch locally? The pulumi nodejs sdk README instructions are not working for me. |
@dustinfarris you should be able to build pulumi/pulumi from these directions https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md |
I followed the instructions, running
Am I missing a step? |
Was this after running the command to install dependencies?
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Without any other changes, pulling in this branch caused pulumi.all.apply calls in our tests to start yielding undefined
for outputs.
sdk/nodejs/runtime/mocks.ts
Outdated
|
||
export interface Mocks { | ||
call(token: string, args: any, provider?: string): any; | ||
newResource(type: string, name: string, inputs: any, provider?: string, id?: string): { id: string, state: any }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it expected that newResource
will need to have a return value for all resources within the program?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes - though I believe you can just return fixed id and no state for anything you haven't yet (or don't plan to) fully mocked.
Curious - what would you want to happen for resources you haven't mocked here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what would you want to happen for resources you haven't mocked here?
I think returning an ID and copying inputs to outputs would be a sensible default
What version of |
Our prod is currently on 1.7.1. Turns out this was a type error on our end that was somehow getting swallowed. |
I made the example work in my environment and using TypeScript. Several random observations:
I would love to discuss this together with my two .NET implementations in #3696 |
I was able to make this a little nicer by writing a helper that promisifies const getOutputs = (desiredOutputs: pulumi.Output<any>[]): Promise<any[]> => {
return new Promise(resolve => {
pulumi.all(desiredOutputs).apply(resolve);
});
};
it("is easier to read and easier to write", async () => {
const [bucket] = await getOutputs([infra.myBucket.bucket]);
assert.equal(bucket, "my-bucket");
}); |
Just to make sure I understand correctly, you're referring to the need to export resources from the TS file, right? |
Yep |
Are we confident enough in this approach that we can merge this? |
@pgavlin Any feedback on #3738 (comment)? Also, I'd love to sync with .NET before we merge, including questions in #3696 (comment) |
sdk/nodejs/runtime/mocks.ts
Outdated
const structproto = require("google-protobuf/google/protobuf/struct_pb.js"); | ||
|
||
export interface Mocks { | ||
call(token: string, args: any, provider?: string): any; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is call()
for? I thought it might be a way to mock things like aws.getCallerIdentity
or secretsmanager.getSecretVersion
but doesn't appear to work that way.
@pgavlin feeling good about the approach — this goes a long way toward alleviating our pain points. A few gaps we've seen:
I don't think any of these are show-stoppers. This PR as-is makes our test suite a lot nicer. Thank you for this. |
These should be mockable by implementing |
This is also surprising-- |
542d422
to
4a8078a
Compare
Ok, I'll try both of these again to rule out user error. |
Mocking export const otherStack = new pulumi.StackReference(
"our-org/other-project/other-stack",
); Then, using setMocks to mock the output: pulumi.runtime.setMocks({
newResource: function(type, name, inputs) {
if (
type === "pulumi:pulumi:StackReference" &&
name === "our-org/other-project/other-stack"
) {
return {
id: "",
state: {
crossAccountRole: "fake-arn",
},
};
}
// ... Then, trying to test that this mock is working: it("has a fake output", done => {
otherStack.getOutput("crossAccountRole").apply(role => {
assert.equal(role, "fake-arn");
done();
});
}); throws this error:
I added some logging and confirmed that |
"data source"-esque calls are hanging for me. If I use pulumi.runtime.setMocks({
newResource: function(type, name, inputs) { /* ... */ },
call: function(token: string, args: any, provider?: string) {
return "foobar";
},
}); and try to test it: it("is mocked", () => {
assert.equal(
aws.secretsmanager.getSecretVersion({ secretId: "foo" }),
"foobar",
);
}); The test hangs with no output. I also tried this with |
4a8078a
to
e9424f7
Compare
Pretty sure this was due to a bug in my implementation. Can you try again on the latest changes?
The shape of the state that
If you change your code to the following, it should work:
|
Provider calls are working now!
StackReference mocks are working! Thanks, Pat! |
return "urn:pulumi:" + [getStack(), getProject(), type, name].join("::"); | ||
} | ||
|
||
public async invoke(req: any, callback: (err: any, innerResponse: any) => void) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's weird to have this be async
but then return a result via a callback. Can we pick one or the other?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The callback is to conform with the gRPC API shape. Would you suggest that I rewrite this in terms of raw promises?
@@ -64,7 +84,7 @@ export function _setIsDryRun(val: boolean) { | |||
* and therefore certain output properties will never be resolved. | |||
*/ | |||
export function isDryRun(): boolean { | |||
return options.dryRun === true || isTestModeEnabled(); | |||
return options.dryRun === true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will this change break folks who are using the old test mode?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It will, yes--do you have a suggestion for avoiding this?
}; | ||
|
||
monitor = mockMonitor; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add tests that use this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure.
e9424f7
to
081b930
Compare
These changes add support for mocking the resource monitor to the NodeJS and Python SDKs. The proposed mock interface is a simplified version of the standard resource monitor that allows an end-user to replace the usual implementations of ReadResource/RegisterResource and Invoke with their own. This can be used in unit tests to allow for precise control of resource outputs and invoke results.
081b930
to
04142be
Compare
These changes add support for mocking the resource monitor to the NodeJS
and Python SDKs. The proposed mock interface is a simplified version of
the standard resource monitor that allows an end-user to replace the
usual implementations of ReadResource/RegisterResource and Invoke with
their own. This can be used in unit tests to allow for precise control
of resource outputs and invoke results.