如何在Go单元测试中模拟Pulumi资源?

huangapple go评论120阅读模式
英文:

How to Mock Pulumi Resources in Go Unit Tests?

问题

我有一个函数,它接受一个 AWS OpenIdConnectProvider Pulumi 资源作为输入,并创建一个带有附加的 AssumeRolePolicy 的 IAM 角色,其中包含来自该 OIDC 提供程序的信息。

问题是:我正在尝试为这个函数编写一个测试,并模拟一个 OIDC 提供程序作为函数调用的输入。我在如何正确模拟这个过程中遇到了困难,目前看起来模拟的数据并不像我期望的那样显示。

看起来我没有正确使用模拟,但我参考了这里的示例(https://github.com/pulumi/examples/blob/master/testing-unit-go/main_test.go)。

更多文档请参考这里(https://www.pulumi.com/docs/guides/testing/unit/)。

  1. package myPkg
  2. import (
  3. "github.com/pulumi/pulumi-aws/sdk/v5/go/aws/iam"
  4. "github.com/pulumi/pulumi/sdk/v3/go/pulumi"
  5. )
  6. func CreateMyCustomRole(ctx *pulumi.Context, name string, oidcProvider *iam.OpenIdConnectProvider, opts ...pulumi.ResourceOption) (*iam.Role, error) {
  7. role := &iam.Role{}
  8. componentURN := fmt.Sprintf("%s-custom-role", name)
  9. err := ctx.RegisterComponentResource("pkg:aws:MyCustomRole", componentURN, role, opts...)
  10. if err != nil {
  11. return nil, err
  12. }
  13. url := oidc.Url.ApplyT(func(s string) string {
  14. return fmt.Sprint(strings.ReplaceAll(s, "https://", ""), ":sub")
  15. }).(pulumi.StringOutput)
  16. assumeRolePolicy := pulumi.Sprintf(`{
  17. "Version": "2012-10-17",
  18. "Statement": [{
  19. "Effect": "Allow",
  20. "Principal": { "Federated": "%s" },
  21. "Action": "sts:AssumeRoleWithWebIdentity",
  22. "Condition": {
  23. "StringEquals": {
  24. "%s": [
  25. "system:serviceaccount:kube-system:*",
  26. "system:serviceaccount:kube-system:cluster-autoscaler"
  27. ]
  28. }
  29. }
  30. }]
  31. }`, oidcProvider.Arn, url)
  32. roleURN := fmt.Sprintf("%s-custom-role", name)
  33. role, err = iam.NewRole(ctx, roleURN, &iam.RoleArgs{
  34. Name: pulumi.String(roleURN),
  35. Description: pulumi.String("Create Custom Role"),
  36. AssumeRolePolicy: assumeRolePolicy,
  37. Tags: pulumi.ToStringMap(map[string]string{"project": "test"}),
  38. })
  39. if err != nil {
  40. return nil, err
  41. }
  42. return role, nil
  43. }
  1. package myPkg
  2. import (
  3. "sync"
  4. "testing"
  5. "github.com/pulumi/pulumi-aws/sdk/v5/go/aws/iam"
  6. "github.com/pulumi/pulumi/sdk/v3/go/common/resource"
  7. "github.com/pulumi/pulumi/sdk/v3/go/pulumi"
  8. "github.com/stretchr/testify/assert"
  9. )
  10. type mocks int
  11. func (mocks) NewResource(args pulumi.MockResourceArgs) (string, resource.PropertyMap, error) {
  12. return args.Name + "_id", args.Inputs, nil
  13. }
  14. func (mocks) Call(args pulumi.MockCallArgs) (resource.PropertyMap, error) {
  15. outputs := map[string]interface{}{}
  16. if args.Token == "aws:iam/getOpenidConnectProvider:getOpenidConnectProvider" {
  17. outputs["arn"] = "arn:aws:iam::123:oidc-provider/oidc.eks.us-west-2.amazonaws.com/id/abc"
  18. outputs["id"] = "abc"
  19. outputs["url"] = "https://someurl"
  20. }
  21. return resource.NewPropertyMapFromMap(outputs), nil
  22. }
  23. func TestCreateMyCustomRole(t *testing.T) {
  24. err := pulumi.RunErr(func(ctx *pulumi.Context) error {
  25. // 获取模拟的 OIDC 提供程序,用作 CreateDefaultAutoscalerRole 的输入
  26. oidc, err := iam.GetOpenIdConnectProvider(ctx, "get-test-oidc-provider", pulumi.ID("abc"), &iam.OpenIdConnectProviderState{})
  27. assert.NoError(t, err)
  28. infra, err := CreateMyCustomRole(ctx, "role1", oidc)
  29. assert.NoError(t, err)
  30. var wg sync.WaitGroup
  31. wg.Add(1)
  32. // 检查1:Assume Role Policy 是否正确格式化
  33. pulumi.All(infra.URN(), infra.AssumeRolePolicy).ApplyT(func(all []interface{}) error {
  34. urn := all[0].(pulumi.URN)
  35. assumeRolePolicy := all[1].(string)
  36. assert.Equal(t, `{
  37. "Version": "2012-10-17",
  38. "Statement": [{
  39. "Effect": "Allow",
  40. "Principal": { "Federated": "arn:aws:iam::123:oidc-provider/oidc.eks.us-west-2.amazonaws.com/id/abc" },
  41. "Action": "sts:AssumeRoleWithWebIdentity",
  42. "Condition": {
  43. "StringEquals": {
  44. "someurl:sub": [
  45. "system:serviceaccount:kube-system:*",
  46. "system:serviceaccount:kube-system:cluster-autoscaler"
  47. ]
  48. }
  49. }
  50. }]
  51. }`, assumeRolePolicy)
  52. wg.Done()
  53. return nil
  54. })
  55. wg.Wait()
  56. return nil
  57. }, pulumi.WithMocks("project", "stack", mocks(0)))
  58. assert.NoError(t, err)
  59. }
  60. Output
  1. Diff:
  2. --- Expected
  3. +++ Actual
  4. @@ -4,3 +4,3 @@
  5. "Effect": "Allow",
  6. - "Principal": { "Federated": "arn:aws:iam::123:oidc-provider/oidc.eks.us-west-2.amazonaws.com/id/abc" },
  7. + "Principal": { "Federated": "" },
  8. "Action": "sts:AssumeRoleWithWebIdentity",
  9. @@ -8,3 +8,3 @@
  10. "StringEquals": {
  11. - "someurl:sub": [
  12. + ":sub": [
  13. "system:serviceaccount:kube-system:*",
  14. "system:serviceaccount:kube-system:cluster-autoscaler"
  15. Test: TestCreateMyCustomRole
  1. <details>
  2. <summary>英文:</summary>
  3. I have a function that takes an input of an AWS OpenIdConnectProvider Pulumi Resource and creates a IAM Role with an AssumeRolePolicy attached that contains info from that OIDC provider.
  4. **The Problem:** I am trying to write a test for this function and mock out a OIDC provider to feed in as input for the function call. I&#39;m having trouble understanding how to properly mock this so that the test output shows what I expect, currently it appears that the mocked data isn&#39;t coming up like I&#39;d expect.
  5. It seems like I&#39;m not using the mock correctly, but I went off the [example here](https://github.com/pulumi/examples/blob/master/testing-unit-go/main_test.go)
  6. [Further Docs here](https://www.pulumi.com/docs/guides/testing/unit/)
  7. ```go
  8. package myPkg
  9. import (
  10. &quot;github.com/pulumi/pulumi-aws/sdk/v5/go/aws/iam&quot;
  11. &quot;github.com/pulumi/pulumi/sdk/v3/go/pulumi&quot;
  12. )
  13. func CreateMyCustomRole(ctx *pulumi.Context, name string, oidcProvider *iam.OpenIdConnectProvider, opts ...pulumi.ResourceOption) (*iam.Role, error) {
  14. role := &amp;iam.Role{}
  15. componentURN := fmt.Sprintf(&quot;%s-custom-role&quot;, name)
  16. err := ctx.RegisterComponentResource(&quot;pkg:aws:MyCustomRole&quot;, componentURN, role, opts...)
  17. if err != nil {
  18. return nil, err
  19. }
  20. url := oidc.Url.ApplyT(func(s string) string {
  21. return fmt.Sprint(strings.ReplaceAll(s, &quot;https://&quot;, &quot;&quot;), &quot;:sub&quot;)
  22. }).(pulumi.StringOutput)
  23. assumeRolePolicy := pulumi.Sprintf(`{
  24. &quot;Version&quot;: &quot;2012-10-17&quot;,
  25. &quot;Statement&quot;: [{
  26. &quot;Effect&quot;: &quot;Allow&quot;,
  27. &quot;Principal&quot;: { &quot;Federated&quot;: &quot;%s&quot; },
  28. &quot;Action&quot;: &quot;sts:AssumeRoleWithWebIdentity&quot;,
  29. &quot;Condition&quot;: {
  30. &quot;StringEquals&quot;: {
  31. &quot;%s&quot;: [
  32. &quot;system:serviceaccount:kube-system:*&quot;,
  33. &quot;system:serviceaccount:kube-system:cluster-autoscaler&quot;
  34. ]
  35. }
  36. }
  37. }]
  38. }`, oidcProvider.Arn, url)
  39. roleURN := fmt.Sprintf(&quot;%s-custom-role&quot;, name)
  40. role, err = iam.NewRole(ctx, roleURN, &amp;iam.RoleArgs{
  41. Name: pulumi.String(roleURN),
  42. Description: pulumi.String(&quot;Create Custom Role&quot;),
  43. AssumeRolePolicy: assumeRolePolicy,
  44. Tags: pulumi.ToStringMap(map[string]string{&quot;project&quot;: &quot;test&quot;}),
  45. })
  46. if err != nil {
  47. return nil, err
  48. }
  49. return role, nil
  50. }
  1. package myPkg
  2. import (
  3. &quot;sync&quot;
  4. &quot;testing&quot;
  5. &quot;github.com/pulumi/pulumi-aws/sdk/v5/go/aws/iam&quot;
  6. &quot;github.com/pulumi/pulumi/sdk/v3/go/common/resource&quot;
  7. &quot;github.com/pulumi/pulumi/sdk/v3/go/pulumi&quot;
  8. &quot;github.com/stretchr/testify/assert&quot;
  9. )
  10. type mocks int
  11. func (mocks) NewResource(args pulumi.MockResourceArgs) (string, resource.PropertyMap, error) {
  12. return args.Name + &quot;_id&quot;, args.Inputs, nil
  13. }
  14. func (mocks) Call(args pulumi.MockCallArgs) (resource.PropertyMap, error) {
  15. outputs := map[string]interface{}{}
  16. if args.Token == &quot;aws:iam/getOpenidConnectProvider:getOpenidConnectProvider&quot; {
  17. outputs[&quot;arn&quot;] = &quot;arn:aws:iam::123:oidc-provider/oidc.eks.us-west-2.amazonaws.com/id/abc&quot;
  18. outputs[&quot;id&quot;] = &quot;abc&quot;
  19. outputs[&quot;url&quot;] = &quot;https://someurl&quot;
  20. }
  21. return resource.NewPropertyMapFromMap(outputs), nil
  22. }
  23. func TestCreateMyCustomRole(t *testing.T) {
  24. err := pulumi.RunErr(func(ctx *pulumi.Context) error {
  25. // Gets the mocked OIDC provider to use as input for the CreateDefaultAutoscalerRole
  26. oidc, err := iam.GetOpenIdConnectProvider(ctx, &quot;get-test-oidc-provider&quot;, pulumi.ID(&quot;abc&quot;), &amp;iam.OpenIdConnectProviderState{})
  27. assert.NoError(t, err)
  28. infra, err := CreateMyCustomRole(ctx, &quot;role1&quot;, oidc})
  29. assert.NoError(t, err)
  30. var wg sync.WaitGroup
  31. wg.Add(1)
  32. // check 1: Assume Role Policy is formatted correctly
  33. pulumi.All(infra.URN(), infra.AssumeRolePolicy).ApplyT(func(all []interface{}) error {
  34. urn := all[0].(pulumi.URN)
  35. assumeRolePolicy := all[1].(string)
  36. assert.Equal(t, `{
  37. &quot;Version&quot;: &quot;2012-10-17&quot;,
  38. &quot;Statement&quot;: [{
  39. &quot;Effect&quot;: &quot;Allow&quot;,
  40. &quot;Principal&quot;: { &quot;Federated&quot;: &quot;arn:aws:iam::123:oidc-provider/oidc.eks.us-west-2.amazonaws.com/id/abc&quot; },
  41. &quot;Action&quot;: &quot;sts:AssumeRoleWithWebIdentity&quot;,
  42. &quot;Condition&quot;: {
  43. &quot;StringEquals&quot;: {
  44. &quot;someurl:sub&quot;: [
  45. &quot;system:serviceaccount:kube-system:*&quot;,
  46. &quot;system:serviceaccount:kube-system:cluster-autoscaler&quot;
  47. ]
  48. }
  49. }
  50. }]
  51. }`, assumeRolePolicy)
  52. wg.Done()
  53. return nil
  54. })
  55. wg.Wait()
  56. return nil
  57. }, pulumi.WithMocks(&quot;project&quot;, &quot;stack&quot;, mocks(0)))
  58. assert.NoError(t, err)
  59. }
  60. Output
  1. Diff:
  2. --- Expected
  3. +++ Actual
  4. @@ -4,3 +4,3 @@
  5. &quot;Effect&quot;: &quot;Allow&quot;,
  6. - &quot;Principal&quot;: { &quot;Federated&quot;: &quot;arn:aws:iam::123:oidc-provider/oidc.eks.us-west-2.amazonaws.com/id/abc&quot; },
  7. + &quot;Principal&quot;: { &quot;Federated&quot;: &quot;&quot; },
  8. &quot;Action&quot;: &quot;sts:AssumeRoleWithWebIdentity&quot;,
  9. @@ -8,3 +8,3 @@
  10. &quot;StringEquals&quot;: {
  11. - &quot;someurl:sub&quot;: [
  12. + &quot;:sub&quot;: [
  13. &quot;system:serviceaccount:kube-system:*&quot;,
  14. Test: TestCreateMyCustomRole
  1. </details>
  2. # 答案1
  3. **得分**: 1
  4. 原来我在使用`NewResource`时弄错了。
  5. 当在测试函数中调用`GetOpenIdConnectProvider`时,它会读取一个资源并创建一个新的资源输出,从而触发对`mocks.NewResource`的调用。
  6. 修复的方法是在`NewResource`函数调用中为`GetOpenIdConnectProvider`返回的资源类型`openIdConnectProvider`定义一个if语句,并使用模拟的输出。
  7. ```go
  8. func (mocks) NewResource(args pulumi.MockResourceArgs) (string, resource.PropertyMap, error) {
  9. pulumi.Printf(args.TypeToken)
  10. outputs := args.Inputs.Mappable()
  11. if args.TypeToken == "aws:iam/openIdConnectProvider:OpenIdConnectProvider" {
  12. outputs["arn"] = "arn:aws:iam::123:oidc-provider/oidc.eks.us-west-2.amazonaws.com/id/abc"
  13. outputs["id"] = "abc"
  14. outputs["url"] = "https://someurl"
  15. }
  16. return args.Name + "_id", resource.NewPropertyMapFromMap(outputs), nil
  17. }
  18. func (mocks) Call(args pulumi.MockCallArgs) (resource.PropertyMap, error) {
  19. outputs := map[string]interface{}{}
  20. return resource.NewPropertyMapFromMap(outputs), nil
  21. }

在下面的代码片段中,我更改了assert,以便它失败并显示对NewResource进行的更改后的差异。

  1. Diff:
  2. --- Expected
  3. +++ Actual
  4. @@ -8,3 +8,3 @@
  5. "StringEquals": {
  6. - "b:sub": [
  7. + "someurl:sub": [
  8. "system:serviceaccount:kube-system:*",
英文:

Turns out I was using the NewResource wrong.

When GetOpenIdConnectProvider is called in the test function it goes to read a resource and create a new resource output which triggers the call to mocks.NewResource

The fix was to instead define an if statement for the resource type returned by the GetOpenIdConnectProvider openIdConnectProviderin the NewResource function call with the mocked outputs.

  1. func (mocks) NewResource(args pulumi.MockResourceArgs) (string, resource.PropertyMap, error) {
  2. pulumi.Printf(args.TypeToken)
  3. outputs := args.Inputs.Mappable()
  4. if args.TypeToken == &quot;aws:iam/openIdConnectProvider:OpenIdConnectProvider&quot; {
  5. outputs[&quot;arn&quot;] = &quot;arn:aws:iam::123:oidc-provider/oidc.eks.us-west-2.amazonaws.com/id/abc&quot;
  6. outputs[&quot;id&quot;] = &quot;abc&quot;
  7. outputs[&quot;url&quot;] = &quot;https://someurl&quot;
  8. }
  9. return args.Name + &quot;_id&quot;, resource.NewPropertyMapFromMap(outputs), nil
  10. }
  11. func (mocks) Call(args pulumi.MockCallArgs) (resource.PropertyMap, error) {
  12. outputs := map[string]interface{}{}
  13. return resource.NewPropertyMapFromMap(outputs), nil
  14. }

Is this below code snippet I changed the assert so that it fails to show what the diff now looks like with the changes made to NewResource above

  1. Diff:
  2. --- Expected
  3. +++ Actual
  4. @@ -8,3 +8,3 @@
  5. &quot;StringEquals&quot;: {
  6. - &quot;b:sub&quot;: [
  7. + &quot;someurl:sub&quot;: [
  8. &quot;system:serviceaccount:kube-system:*&quot;,

huangapple
  • 本文由 发表于 2023年4月14日 03:31:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/76009185.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定