@ -3,6 +3,7 @@ package schema
import (
"fmt"
"reflect"
"strconv"
"testing"
"github.com/hashicorp/terraform/terraform"
@ -478,3 +479,218 @@ func TestResourceRefresh_noExists(t *testing.T) {
t . Fatalf ( "should have no state" )
}
}
func TestResourceRefresh_needsMigration ( t * testing . T ) {
// Schema v2 it deals only in newfoo, which tracks foo as an int
r := & Resource {
SchemaVersion : 2 ,
Schema : map [ string ] * Schema {
"newfoo" : & Schema {
Type : TypeInt ,
Optional : true ,
} ,
} ,
}
r . Read = func ( d * ResourceData , m interface { } ) error {
return d . Set ( "newfoo" , d . Get ( "newfoo" ) . ( int ) + 1 )
}
r . MigrateState = func (
v int ,
s * terraform . InstanceState ,
meta interface { } ) ( * terraform . InstanceState , error ) {
// Real state migration functions will probably switch on this value,
// but we'll just assert on it for now.
if v != 1 {
t . Fatalf ( "Expected StateSchemaVersion to be 1, got %d" , v )
}
if meta != 42 {
t . Fatal ( "Expected meta to be passed through to the migration function" )
}
oldfoo , err := strconv . ParseFloat ( s . Attributes [ "oldfoo" ] , 64 )
if err != nil {
t . Fatalf ( "err: %#v" , err )
}
s . Attributes [ "newfoo" ] = strconv . Itoa ( ( int ( oldfoo * 10 ) ) )
delete ( s . Attributes , "oldfoo" )
return s , nil
}
// State is v1 and deals in oldfoo, which tracked foo as a float at 1/10th
// the scale of newfoo
s := & terraform . InstanceState {
ID : "bar" ,
Attributes : map [ string ] string {
"oldfoo" : "1.2" ,
} ,
Meta : map [ string ] string {
"schema_version" : "1" ,
} ,
}
actual , err := r . Refresh ( s , 42 )
if err != nil {
t . Fatalf ( "err: %s" , err )
}
expected := & terraform . InstanceState {
ID : "bar" ,
Attributes : map [ string ] string {
"id" : "bar" ,
"newfoo" : "13" ,
} ,
Meta : map [ string ] string {
"schema_version" : "2" ,
} ,
}
if ! reflect . DeepEqual ( actual , expected ) {
t . Fatalf ( "bad:\n\nexpected: %#v\ngot: %#v" , expected , actual )
}
}
func TestResourceRefresh_noMigrationNeeded ( t * testing . T ) {
r := & Resource {
SchemaVersion : 2 ,
Schema : map [ string ] * Schema {
"newfoo" : & Schema {
Type : TypeInt ,
Optional : true ,
} ,
} ,
}
r . Read = func ( d * ResourceData , m interface { } ) error {
return d . Set ( "newfoo" , d . Get ( "newfoo" ) . ( int ) + 1 )
}
r . MigrateState = func (
v int ,
s * terraform . InstanceState ,
meta interface { } ) ( * terraform . InstanceState , error ) {
t . Fatal ( "Migrate function shouldn't be called!" )
return nil , nil
}
s := & terraform . InstanceState {
ID : "bar" ,
Attributes : map [ string ] string {
"newfoo" : "12" ,
} ,
Meta : map [ string ] string {
"schema_version" : "2" ,
} ,
}
actual , err := r . Refresh ( s , nil )
if err != nil {
t . Fatalf ( "err: %s" , err )
}
expected := & terraform . InstanceState {
ID : "bar" ,
Attributes : map [ string ] string {
"id" : "bar" ,
"newfoo" : "13" ,
} ,
Meta : map [ string ] string {
"schema_version" : "2" ,
} ,
}
if ! reflect . DeepEqual ( actual , expected ) {
t . Fatalf ( "bad:\n\nexpected: %#v\ngot: %#v" , expected , actual )
}
}
func TestResourceRefresh_stateSchemaVersionUnset ( t * testing . T ) {
r := & Resource {
// Version 1 > Version 0
SchemaVersion : 1 ,
Schema : map [ string ] * Schema {
"newfoo" : & Schema {
Type : TypeInt ,
Optional : true ,
} ,
} ,
}
r . Read = func ( d * ResourceData , m interface { } ) error {
return d . Set ( "newfoo" , d . Get ( "newfoo" ) . ( int ) + 1 )
}
r . MigrateState = func (
v int ,
s * terraform . InstanceState ,
meta interface { } ) ( * terraform . InstanceState , error ) {
s . Attributes [ "newfoo" ] = s . Attributes [ "oldfoo" ]
return s , nil
}
s := & terraform . InstanceState {
ID : "bar" ,
Attributes : map [ string ] string {
"oldfoo" : "12" ,
} ,
}
actual , err := r . Refresh ( s , nil )
if err != nil {
t . Fatalf ( "err: %s" , err )
}
expected := & terraform . InstanceState {
ID : "bar" ,
Attributes : map [ string ] string {
"id" : "bar" ,
"newfoo" : "13" ,
} ,
Meta : map [ string ] string {
"schema_version" : "1" ,
} ,
}
if ! reflect . DeepEqual ( actual , expected ) {
t . Fatalf ( "bad:\n\nexpected: %#v\ngot: %#v" , expected , actual )
}
}
func TestResourceRefresh_migrateStateErr ( t * testing . T ) {
r := & Resource {
SchemaVersion : 2 ,
Schema : map [ string ] * Schema {
"newfoo" : & Schema {
Type : TypeInt ,
Optional : true ,
} ,
} ,
}
r . Read = func ( d * ResourceData , m interface { } ) error {
t . Fatal ( "Read should never be called!" )
return nil
}
r . MigrateState = func (
v int ,
s * terraform . InstanceState ,
meta interface { } ) ( * terraform . InstanceState , error ) {
return s , fmt . Errorf ( "triggering an error" )
}
s := & terraform . InstanceState {
ID : "bar" ,
Attributes : map [ string ] string {
"oldfoo" : "12" ,
} ,
}
_ , err := r . Refresh ( s , nil )
if err == nil {
t . Fatal ( "expected error, but got none!" )
}
}