Override provider schemas for mocked providers (#34481)

* Override provider schemas for mocked providers

* address comments
pull/34487/head
Liam Cervante 2 years ago committed by GitHub
parent 3a75362cce
commit bd40c411f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -41,6 +41,23 @@ func (m *Mock) GetProviderSchema() GetProviderSchemaResponse {
if m.schema == nil {
// Cache the schema, it's not changing.
schema := m.Provider.GetProviderSchema()
// Override the provider schema with the constant mock provider schema.
// This is empty at the moment, check configs/mock_provider.go for the
// actual schema.
//
// The GetProviderSchemaResponse is returned by value, so it should be
// safe for us to modify directly, without affecting any shared state
// that could be in use elsewhere.
schema.Provider = Schema{
Version: schema.Provider.Version,
Block: nil, // Empty - we support no blocks or attributes in mock provider configurations.
}
// Note, we leave the resource and data source schemas as they are since
// we want to be able to validate those configurations against the real
// provider schemas.
m.schema = &schema
}
return *m.schema

@ -2492,6 +2492,151 @@ resource "test_object" "foo" {
}
}
func TestContext2Apply_mockProviderRequiredSchema(t *testing.T) {
m := testModuleInline(t, map[string]string{
"main.tf": `
provider "test" {}
data "test_object" "foo" {}
resource "test_object" "foo" {
value = data.test_object.foo.output
}
`,
})
// Manually mark the provider config as being mocked.
m.Module.ProviderConfigs["test"].Mock = true
m.Module.ProviderConfigs["test"].MockData = &configs.MockData{
MockDataSources: map[string]*configs.MockResource{
"test_object": {
Mode: addrs.DataResourceMode,
Type: "test_object",
Defaults: cty.ObjectVal(map[string]cty.Value{
"output": cty.StringVal("expected data output"),
}),
},
},
MockResources: map[string]*configs.MockResource{
"test_object": {
Mode: addrs.ManagedResourceMode,
Type: "test_object",
Defaults: cty.ObjectVal(map[string]cty.Value{
"output": cty.StringVal("expected resource output"),
}),
},
},
}
// This time our test provider has a required attribute that we don't
// provide in the configuration. The fact we've marked this provider as a
// mock means the missing required attribute doesn't matter.
testProvider := &MockProvider{
GetProviderSchemaResponse: &providers.GetProviderSchemaResponse{
Provider: providers.Schema{
Block: &configschema.Block{
Attributes: map[string]*configschema.Attribute{
"required": {
Type: cty.String,
Required: true,
},
},
},
},
ResourceTypes: map[string]providers.Schema{
"test_object": {
Block: &configschema.Block{
Attributes: map[string]*configschema.Attribute{
"value": {
Type: cty.String,
Required: true,
},
"output": {
Type: cty.String,
Computed: true,
},
},
},
},
},
DataSources: map[string]providers.Schema{
"test_object": {
Block: &configschema.Block{
Attributes: map[string]*configschema.Attribute{
"output": {
Type: cty.String,
Computed: true,
},
},
},
},
},
},
}
reachedReadDataSourceFn := false
reachedPlanResourceChangeFn := false
reachedApplyResourceChangeFn := false
testProvider.ReadDataSourceFn = func(request providers.ReadDataSourceRequest) (resp providers.ReadDataSourceResponse) {
reachedReadDataSourceFn = true
cfg := request.Config.AsValueMap()
cfg["output"] = cty.StringVal("unexpected data output")
resp.State = cty.ObjectVal(cfg)
return resp
}
testProvider.PlanResourceChangeFn = func(request providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) {
reachedPlanResourceChangeFn = true
cfg := request.Config.AsValueMap()
cfg["output"] = cty.UnknownVal(cty.String)
resp.PlannedState = cty.ObjectVal(cfg)
return resp
}
testProvider.ApplyResourceChangeFn = func(request providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
reachedApplyResourceChangeFn = true
cfg := request.Config.AsValueMap()
cfg["output"] = cty.StringVal("unexpected resource output")
resp.NewState = cty.ObjectVal(cfg)
return resp
}
ctx := testContext2(t, &ContextOpts{
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(testProvider),
},
})
plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{
Mode: plans.NormalMode,
})
if diags.HasErrors() {
t.Fatalf("expected no errors, but got %s", diags)
}
state, diags := ctx.Apply(plan, m, nil)
if diags.HasErrors() {
t.Fatalf("expected no errors, but got %s", diags)
}
// Check we never made it to the actual provider.
if reachedReadDataSourceFn {
t.Errorf("read the data source in the provider when it should have been mocked")
}
if reachedPlanResourceChangeFn {
t.Errorf("planned the resource in the provider when it should have been mocked")
}
if reachedApplyResourceChangeFn {
t.Errorf("applied the resource in the provider when it should have been mocked")
}
// Check we got the right data back from our mocked provider.
instance := state.ResourceInstance(mustResourceInstanceAddr("test_object.foo"))
expected := "{\"output\":\"expected resource output\",\"value\":\"expected data output\"}"
if diff := cmp.Diff(string(instance.Current.AttrsJSON), expected); len(diff) > 0 {
t.Errorf("expected:\n%s\nactual:\n%s\ndiff:\n%s", expected, string(instance.Current.AttrsJSON), diff)
}
}
func TestContext2Apply_forget(t *testing.T) {
addrA := mustResourceInstanceAddr("test_object.a")
m := testModuleInline(t, map[string]string{

Loading…
Cancel
Save