diff --git a/.github/workflows/nightly_smoke_tests.yml b/.github/workflows/nightly_smoke_tests.yml index b400020ed..acc45fa0a 100644 --- a/.github/workflows/nightly_smoke_tests.yml +++ b/.github/workflows/nightly_smoke_tests.yml @@ -15,7 +15,7 @@ jobs: with: ref: dev - - name: Checkout code + - name: Set up go uses: actions/setup-go@v4 with: go-version: '1.19' diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml new file mode 100644 index 000000000..6e9dbb013 --- /dev/null +++ b/.github/workflows/unit-tests.yml @@ -0,0 +1,22 @@ +name: Unit Tests +on: + workflow_dispatch: null + push: + pull_request: +jobs: + unit_tests: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: '1.20' + + - run: go version + + - name: Run unit tests + run: make unittest \ No newline at end of file diff --git a/linode/accountlogins/framework_models_unit_test.go b/linode/accountlogins/framework_models_unit_test.go new file mode 100644 index 000000000..a2196a2c2 --- /dev/null +++ b/linode/accountlogins/framework_models_unit_test.go @@ -0,0 +1,44 @@ +//go:build unit + +package accountlogins + +import ( + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/linode/linodego" + "testing" + "time" +) + +func TestParseLogins(t *testing.T) { + model := &AccountLoginFilterModel{} + + // Sample input login data + login1 := linodego.Login{ + ID: 1, + Datetime: &time.Time{}, + IP: "127.0.0.1", + Restricted: false, + Username: "user1", + Status: "success", + } + + login2 := linodego.Login{ + ID: 2, + Datetime: &time.Time{}, + IP: "192.168.1.1", + Restricted: true, + Username: "user2", + Status: "failure", + } + + model.parseLogins([]linodego.Login{login1, login2}) + + if len(model.Logins) != 2 { + t.Errorf("Expected %d logins, but got %d", 2, len(model.Logins)) + } + + // Check if the fields of the first login in the model have been populated correctly + if model.Logins[0].ID != types.Int64Value(1) { + t.Errorf("Expected ID to be %d, but got %d", 1, model.Logins[0].ID) + } +} diff --git a/linode/accountsettings/framework_model_unit_test.go b/linode/accountsettings/framework_model_unit_test.go new file mode 100644 index 000000000..31dee1f6b --- /dev/null +++ b/linode/accountsettings/framework_model_unit_test.go @@ -0,0 +1,59 @@ +//go:build unit + +package accountsettings + +import ( + "github.com/linode/linodego" + "testing" + + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func TestParseAccountSettings(t *testing.T) { + // Create mock AccountSettings data + mockEmail := "test@example.com" + longviewSubscriptionValue := "longview-3" + objectStorageValue := "active" + backupsEnabledValue := true + managedValue := true + networkHelperValue := false + + mockSettings := &linodego.AccountSettings{ + BackupsEnabled: backupsEnabledValue, + Managed: managedValue, + NetworkHelper: networkHelperValue, + LongviewSubscription: &longviewSubscriptionValue, + ObjectStorage: &objectStorageValue, + } + + // Create a mock AccountSettingsModel instance + model := &AccountSettingsModel{} + + // Call the parseAccountSettings function + model.parseAccountSettings(mockEmail, mockSettings) + + // Check if the fields in the model have been populated correctly + if model.ID != types.StringValue(mockEmail) { + t.Errorf("Expected ID to be %s, but got %s", mockEmail, model.ID) + } + + if model.LongviewSubscription != types.StringValue("longview-3") { + t.Errorf("Expected LongviewSubscription to be %s, but got %s", "longview-3", model.LongviewSubscription) + } + + if model.ObjectStorage != types.StringValue("active") { + t.Errorf("Expected ObjectStorage to be %s, but got %s", "active", model.ObjectStorage) + } + + if model.BackupsEnabed != types.BoolValue(true) { + t.Errorf("Expected BackupsEnabed to be %v, but got %v", true, model.BackupsEnabed) + } + + if model.Managed != types.BoolValue(true) { + t.Errorf("Expected Managed to be %v, but got %v", true, model.Managed) + } + + if model.NetworkHelper != types.BoolValue(false) { + t.Errorf("Expected NetworkHelper to be %v, but got %v", false, model.NetworkHelper) + } +} diff --git a/linode/backup/framework_model_unit_test.go b/linode/backup/framework_model_unit_test.go new file mode 100644 index 000000000..80109ca5b --- /dev/null +++ b/linode/backup/framework_model_unit_test.go @@ -0,0 +1,50 @@ +//go:build unit + +package backup + +import ( + "context" + "github.com/hashicorp/terraform-plugin-framework/types" + "testing" + + "github.com/linode/linodego" + "github.com/stretchr/testify/assert" +) + +func TestParseBackups(t *testing.T) { + ctx := context.Background() + + // Create mock data for InstanceSnapshot + mockSnapshot := &linodego.InstanceSnapshot{ + ID: 1, + Label: "Linode Snapshot Label", + Status: "successful", + Type: "snapshot", + Created: nil, + Updated: nil, + Finished: nil, + Configs: []string{"config1", "config2"}, + Disks: []*linodego.InstanceSnapshotDisk{}, // You can populate this with mock disk data + Available: true, + } + + mockBackupSnapshotResponse := &linodego.InstanceBackupSnapshotResponse{ + Current: mockSnapshot, + InProgress: mockSnapshot, + } + + mockBackups := &linodego.InstanceBackupsResponse{ + Automatic: []*linodego.InstanceSnapshot{mockSnapshot}, + Snapshot: mockBackupSnapshotResponse, + } + + linodeId := int64(123) + + data := &DataSourceModel{} + + diags := data.parseBackups(ctx, mockBackups, types.Int64Value(linodeId)) + + assert.False(t, diags.HasError(), "Expected no errors in diagnostics") + + assert.Equal(t, types.Int64Value(linodeId), data.ID) +} diff --git a/linode/databasebackups/framework_models_unit_test.go b/linode/databasebackups/framework_models_unit_test.go new file mode 100644 index 000000000..0cf259b00 --- /dev/null +++ b/linode/databasebackups/framework_models_unit_test.go @@ -0,0 +1,119 @@ +//go:build unit + +package databasebackups + +import ( + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/linode/linodego" + "github.com/stretchr/testify/assert" + "testing" + "time" +) + +func TestParseMySQLBackup(t *testing.T) { + mockBackup := linodego.MySQLDatabaseBackup{ + ID: 123, + Label: "Scheduled - 02/04/22 11:11 UTC-XcCRmI", + Type: "manual", + Created: &time.Time{}, + } + + model := DatabaseBackupModel{} + + model.ParseMySQLBackup(mockBackup) + + assert.Equal(t, types.Int64Value(123), model.ID) + assert.Equal(t, types.StringValue("Scheduled - 02/04/22 11:11 UTC-XcCRmI"), model.Label) + assert.Equal(t, types.StringValue("manual"), model.Type) + assert.NotNil(t, model.Created) // Created field should not be nil + + expectedFormattedTime := mockBackup.Created.Format(time.RFC3339) + assert.Equal(t, types.StringValue(expectedFormattedTime), model.Created) +} + +func TestParseMySQLBackups(t *testing.T) { + mockBackups := []linodego.MySQLDatabaseBackup{ + { + ID: 1, + Label: "Scheduled - 02/07/22 11:18 UTC-XcCRmI", + Type: "manual", + Created: &time.Time{}, + }, + { + ID: 2, + Label: "Scheduled - 02/07/22 11:18 UTC-XcCRmI", + Type: "auto", + Created: &time.Time{}, + }, + } + + model := DatabaseBackupFilterModel{} + + model.parseMySQLBackups(mockBackups) + + assert.Len(t, model.Backups, len(mockBackups)) + + assert.Equal(t, types.Int64Value(1), model.Backups[0].ID) + assert.Equal(t, types.StringValue("Scheduled - 02/07/22 11:18 UTC-XcCRmI"), model.Backups[0].Label) + assert.Equal(t, types.StringValue("manual"), model.Backups[0].Type) + assert.NotNil(t, model.Backups[0].Created) + + assert.Equal(t, types.Int64Value(2), model.Backups[1].ID) + assert.Equal(t, types.StringValue("Scheduled - 02/07/22 11:18 UTC-XcCRmI"), model.Backups[1].Label) + assert.Equal(t, types.StringValue("auto"), model.Backups[1].Type) + assert.NotNil(t, model.Backups[1].Created) +} + +func TestParsePostgresSQLBackup(t *testing.T) { + mockBackup := linodego.PostgresDatabaseBackup{ + ID: 123, + Label: "Postgres Backup", + Type: "auto", + Created: &time.Time{}, + } + + model := DatabaseBackupModel{} + + model.ParsePostgresSQLBackup(mockBackup) + + assert.Equal(t, types.Int64Value(123), model.ID) + assert.Equal(t, types.StringValue("Postgres Backup"), model.Label) + assert.Equal(t, types.StringValue("auto"), model.Type) + assert.NotNil(t, model.Created) // Created field should not be nil + + expectedFormattedTime := mockBackup.Created.Format(time.RFC3339) + assert.Equal(t, types.StringValue(expectedFormattedTime), model.Created) +} + +func TestParsePostgresSQLBackups(t *testing.T) { + mockBackups := []linodego.PostgresDatabaseBackup{ + { + ID: 1, + Label: "Postgres Backup 1", + Type: "manual", + Created: &time.Time{}, + }, + { + ID: 2, + Label: "Postgres Backup 2", + Type: "auto", + Created: &time.Time{}, + }, + } + + model := DatabaseBackupFilterModel{} + + model.parsePostgresSQLBackups(mockBackups) + + assert.Len(t, model.Backups, len(mockBackups)) + + assert.Equal(t, types.Int64Value(1), model.Backups[0].ID) + assert.Equal(t, types.StringValue("Postgres Backup 1"), model.Backups[0].Label) + assert.Equal(t, types.StringValue("manual"), model.Backups[0].Type) + assert.NotNil(t, model.Backups[0].Created) + + assert.Equal(t, types.Int64Value(2), model.Backups[1].ID) + assert.Equal(t, types.StringValue("Postgres Backup 2"), model.Backups[1].Label) + assert.Equal(t, types.StringValue("auto"), model.Backups[1].Type) + assert.NotNil(t, model.Backups[1].Created) +} diff --git a/linode/databaseengines/framework_models_unit_test.go b/linode/databaseengines/framework_models_unit_test.go new file mode 100644 index 000000000..8bb18ec2c --- /dev/null +++ b/linode/databaseengines/framework_models_unit_test.go @@ -0,0 +1,40 @@ +//go:build unit + +package databaseengines + +import ( + "github.com/hashicorp/terraform-plugin-framework/types" + "testing" + + "github.com/linode/linodego" + "github.com/stretchr/testify/assert" +) + +func TestParseEngines(t *testing.T) { + mockEngines := []linodego.DatabaseEngine{ + { + ID: "mysql/8.0.26", + Engine: "mysql", + Version: "8.0.26", + }, + { + ID: "postgresql/13.0.26", + Engine: "postgresql", + Version: "13.0.26", + }, + } + + model := DatabaseEngineFilterModel{} + + model.parseEngines(mockEngines) + + assert.Len(t, model.Engines, len(mockEngines)) + + assert.Equal(t, types.StringValue("mysql/8.0.26"), model.Engines[0].ID) + assert.Equal(t, types.StringValue("mysql"), model.Engines[0].Engine) + assert.Equal(t, types.StringValue("8.0.26"), model.Engines[0].Version) + + assert.Equal(t, types.StringValue("postgresql/13.0.26"), model.Engines[1].ID) + assert.Equal(t, types.StringValue("postgresql"), model.Engines[1].Engine) + assert.Equal(t, types.StringValue("13.0.26"), model.Engines[1].Version) +} diff --git a/linode/databasemysql/framework_models_unit_test.go b/linode/databasemysql/framework_models_unit_test.go new file mode 100644 index 000000000..3eb68cf00 --- /dev/null +++ b/linode/databasemysql/framework_models_unit_test.go @@ -0,0 +1,93 @@ +//go:build unit + +package databasemysql + +import ( + "context" + "testing" + "time" + + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/linode/linodego" + "github.com/stretchr/testify/assert" +) + +func TestParseMySQLDatabase(t *testing.T) { + mockDB := &linodego.MySQLDatabase{ + ID: 123, + Status: "active", + Label: "example-db", + Hosts: linodego.DatabaseHost{ + Primary: "lin-123-456-mysql-mysql-primary.servers.linodedb.net", + Secondary: "lin-123-456-mysql-primary-private.servers.linodedb.net", + }, + Region: "us-east", + Type: "g6-dedicated-2", + Engine: "mysql", + Version: "8.0.26", + ClusterSize: 3, + ReplicationType: "semi_synch", + SSLConnection: true, + Encrypted: false, + AllowList: []string{"203.0.113.1/32", "192.0.1.0/24"}, + Created: &time.Time{}, + Updated: &time.Time{}, + Updates: linodego.DatabaseMaintenanceWindow{ + DayOfWeek: 1, + Duration: 3, + Frequency: "weekly", + HourOfDay: 0, + WeekOfMonth: nil, + }, + } + + data := &DataSourceModel{} + diagnostics := data.parseMySQLDatabase(context.Background(), mockDB) + + assert.False(t, diagnostics.HasError(), "Expected no error") + + assert.Equal(t, types.Int64Value(123), data.DatabaseID) + assert.Equal(t, types.StringValue("active"), data.Status) + assert.Equal(t, types.StringValue("example-db"), data.Label) + assert.Equal(t, types.StringValue("lin-123-456-mysql-mysql-primary.servers.linodedb.net"), data.HostPrimary) + assert.Equal(t, types.StringValue("lin-123-456-mysql-primary-private.servers.linodedb.net"), data.HostSecondary) + assert.Equal(t, types.StringValue("us-east"), data.Region) + assert.Equal(t, types.StringValue("g6-dedicated-2"), data.Type) + assert.Equal(t, types.StringValue("mysql"), data.Engine) + assert.Equal(t, types.StringValue("8.0.26"), data.Version) + assert.Equal(t, types.Int64Value(3), data.ClusterSize) + assert.Equal(t, types.StringValue("semi_synch"), data.ReplicationType) + assert.Equal(t, types.BoolValue(true), data.SSLConnection) + assert.Equal(t, types.BoolValue(false), data.Encrypted) + assert.Contains(t, data.AllowList.String(), "203.0.113.1/32") + assert.Contains(t, data.AllowList.String(), "192.0.1.0/24") + + assert.Contains(t, data.Updates.String(), "sunday") + assert.Contains(t, data.Updates.String(), "3") + assert.Contains(t, data.Updates.String(), "weekly") + assert.Contains(t, data.Updates.String(), "0") +} + +func TestParseMySQLDatabaseSSL(t *testing.T) { + mockDBSSL := &linodego.MySQLDatabaseSSL{ + CACertificate: []byte("-----BEGIN CERTIFICATE-----\nMIIDdTCCAl2gAwIBAgIUT01...\n...u4QIDAQABo1MwUTAdBgNV...\n-----END CERTIFICATE-----"), + } + + data := &DataSourceModel{} + data.parseMySQLDatabaseSSL(mockDBSSL) + + assert.Equal(t, types.StringValue("-----BEGIN CERTIFICATE-----\nMIIDdTCCAl2gAwIBAgIUT01...\n...u4QIDAQABo1MwUTAdBgNV...\n-----END CERTIFICATE-----"), data.CACert) +} + +func TestParseMySQLDatabaseCredentials(t *testing.T) { + mockDBCred := &linodego.MySQLDatabaseCredential{ + Username: "linode_sqldb_user", + Password: "password123", + } + + data := &DataSourceModel{} + data.parseMySQLDatabaseCredentials(mockDBCred) + + assert.Equal(t, types.StringValue("linode_sqldb_user"), data.RootUsername) + assert.Equal(t, types.StringValue("password123"), data.RootPassword) +} diff --git a/linode/databasepostgresql/framework_models_unit_test.go b/linode/databasepostgresql/framework_models_unit_test.go new file mode 100644 index 000000000..af0829de6 --- /dev/null +++ b/linode/databasepostgresql/framework_models_unit_test.go @@ -0,0 +1,88 @@ +//go:build unit + +package databasepostgresql + +import ( + "context" + "testing" + "time" + + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/linode/linodego" + "github.com/stretchr/testify/assert" +) + +func TestParsePostgresDatabase(t *testing.T) { + mockDatabase := linodego.PostgresDatabase{ + ID: 123, + Status: "active", + Label: "example-db", + Region: "us-east", + Type: "g6-dedicated-2", + Engine: "postgresql", + Version: "13.2", + Encrypted: false, + AllowList: []string{"203.0.113.1/32", "192.0.1.0/24"}, + Port: 3306, + SSLConnection: true, + ClusterSize: 3, + ReplicationCommitType: "local", + ReplicationType: "async", + Hosts: linodego.DatabaseHost{ + Primary: "lin-0000-000-pgsql-primary.servers.linodedb.net", + Secondary: "lin-0000-000-pgsql-primary-private.servers.linodedb.net", + }, + Updates: linodego.DatabaseMaintenanceWindow{ + DayOfWeek: 1, + Duration: 3, + Frequency: "weekly", + HourOfDay: 0, + WeekOfMonth: nil, + }, + Created: &time.Time{}, + Updated: &time.Time{}, + } + + data := &DataSourceModel{} + diagnostics := data.parsePostgresDatabase(context.Background(), &mockDatabase) + assert.False(t, diagnostics.HasError(), "Expected no error") + + assert.Equal(t, types.Int64Value(123), data.DatabaseID) + assert.Equal(t, types.StringValue("active"), data.Status) + assert.Equal(t, types.StringValue("example-db"), data.Label) + + assert.Contains(t, data.AllowList.String(), "203.0.113.1/32") + assert.Contains(t, data.AllowList.String(), "192.0.1.0/24") + + assert.Equal(t, types.StringValue("lin-0000-000-pgsql-primary.servers.linodedb.net"), data.HostPrimary) + assert.Equal(t, types.StringValue("lin-0000-000-pgsql-primary-private.servers.linodedb.net"), data.HostSecondary) + + assert.Contains(t, data.Updates.String(), "sunday") + assert.Contains(t, data.Updates.String(), "3") + assert.Contains(t, data.Updates.String(), "weekly") + assert.Contains(t, data.Updates.String(), "0") +} + +func TestParsePostgresDatabaseSSL(t *testing.T) { + mockSSL := linodego.PostgresDatabaseSSL{ + CACertificate: []byte("-----BEGIN CERTIFICATE-----\nMIIDdTCCAl2gAwIBAgIUT01...\n...u4QIDAQABo1MwUTAdBgNV...\n-----END CERTIFICATE-----"), + } + + data := &DataSourceModel{} + data.parsePostgresDatabaseSSL(&mockSSL) + + assert.Equal(t, types.StringValue("-----BEGIN CERTIFICATE-----\nMIIDdTCCAl2gAwIBAgIUT01...\n...u4QIDAQABo1MwUTAdBgNV...\n-----END CERTIFICATE-----"), data.CACert) +} + +func TestParsePostgresDatabaseCredentials(t *testing.T) { + mockCredential := linodego.PostgresDatabaseCredential{ + Username: "linode_postgresql_user", + Password: "password123", + } + + data := &DataSourceModel{} + data.parsePostgresDatabaseCredentials(&mockCredential) + + assert.Equal(t, types.StringValue("linode_postgresql_user"), data.RootUsername) + assert.Equal(t, types.StringValue("password123"), data.RootPassword) +} diff --git a/linode/databases/framework_models_unit_test.go b/linode/databases/framework_models_unit_test.go new file mode 100644 index 000000000..ce152bd7a --- /dev/null +++ b/linode/databases/framework_models_unit_test.go @@ -0,0 +1,67 @@ +//go:build unit + +package databases + +import ( + "testing" + "time" + + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/linode/linodego" + "github.com/stretchr/testify/assert" +) + +func TestParseDatabases(t *testing.T) { + mockDB1 := linodego.Database{ + ID: 123, + Status: linodego.DatabaseStatusActive, + Label: "example-db-1", + Region: "us-east", + Type: "g6-standard-1", + Engine: "mysql", + Version: "8.0", + Encrypted: false, + AllowList: []string{"203.0.113.1/32", "192.0.1.0/24"}, + Hosts: linodego.DatabaseHost{Primary: "primary.example.com", Secondary: "secondary.example.com"}, + InstanceURI: "mysql://user:pass@primary.example.com:3306/db", + ReplicationType: "standard", + SSLConnection: true, + Created: &time.Time{}, + Updated: &time.Time{}, + } + + mockDB2 := linodego.Database{ + ID: 456, + Status: linodego.DatabaseStatusProvisioning, + Label: "example-db-2", + Region: "us-central", + Type: "g6-standard-2", + Engine: "postgresql", + Version: "13", + Encrypted: true, + AllowList: []string{"10.0.0.1/32"}, + Hosts: linodego.DatabaseHost{Primary: "primary-pg.example.com", Secondary: "secondary-pg.example.com"}, + InstanceURI: "postgresql://user:pass@primary-pg.example.com:5432/db", + ReplicationType: "standard", + SSLConnection: false, + Created: &time.Time{}, + Updated: &time.Time{}, + } + + mockDatabases := []linodego.Database{mockDB1, mockDB2} + + model := &DatabaseFilterModel{} + model.parseDatabases(mockDatabases) + + assert.Len(t, model.Databases, 2) + + // Database 1 Assertions + assert.Equal(t, types.Int64Value(123), model.Databases[0].ID) + assert.Equal(t, types.StringValue("active"), model.Databases[0].Status) + assert.Equal(t, types.StringValue("example-db-1"), model.Databases[0].Label) + + // Database 2 Assertions + assert.Equal(t, types.Int64Value(456), model.Databases[1].ID) + assert.Equal(t, types.StringValue("provisioning"), model.Databases[1].Status) + assert.Equal(t, types.StringValue("example-db-2"), model.Databases[1].Label) +} diff --git a/linode/domain/framework_models_unit_test.go b/linode/domain/framework_models_unit_test.go new file mode 100644 index 000000000..7e41735c2 --- /dev/null +++ b/linode/domain/framework_models_unit_test.go @@ -0,0 +1,48 @@ +//go:build unit + +package domain + +import ( + "github.com/hashicorp/terraform-plugin-framework/types" + "testing" + + "github.com/linode/linodego" + "github.com/stretchr/testify/assert" +) + +func TestParseDomain(t *testing.T) { + mockDomain := &linodego.Domain{ + ID: 1234, + Domain: "example.org", + Type: "master", + Status: "active", + Description: "description", + SOAEmail: "admin@example.org", + RetrySec: 300, + MasterIPs: nil, + AXfrIPs: nil, + Tags: []string{"example tag", "another example"}, + ExpireSec: 300, + RefreshSec: 300, + TTLSec: 300, + } + + domainModel := &DomainModel{} + + domainModel.parseDomain(mockDomain) + + assert.Equal(t, types.Int64Value(1234), domainModel.ID) + assert.Equal(t, types.StringValue("example.org"), domainModel.Domain) + assert.Equal(t, types.StringValue("master"), domainModel.Type) + assert.Equal(t, types.StringValue("active"), domainModel.Status) + assert.Equal(t, types.StringValue("description"), domainModel.Description) + assert.Equal(t, types.StringValue("admin@example.org"), domainModel.SOAEmail) + assert.Equal(t, types.Int64Value(300), domainModel.RetrySec) + assert.Empty(t, domainModel.MasterIPs) + assert.Empty(t, domainModel.AXFRIPs) + assert.Contains(t, domainModel.Tags, types.StringValue("example tag")) + assert.Contains(t, domainModel.Tags, types.StringValue("another example")) + assert.Equal(t, types.Int64Value(300), domainModel.ExpireSec) + assert.Equal(t, types.Int64Value(300), domainModel.RefreshSec) + assert.Equal(t, types.Int64Value(300), domainModel.TTLSec) +} diff --git a/linode/domainzonefile/framework_datasource_unit_test.go b/linode/domainzonefile/framework_datasource_unit_test.go new file mode 100644 index 000000000..bb87e3425 --- /dev/null +++ b/linode/domainzonefile/framework_datasource_unit_test.go @@ -0,0 +1,40 @@ +//go:build unit + +package domainzonefile + +import ( + "context" + "encoding/json" + "testing" + + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/linode/linodego" + "github.com/stretchr/testify/assert" +) + +func TestParseDomainZoneFile(t *testing.T) { + mockZoneFileData := linodego.DomainZoneFile{ + ZoneFile: []string{ + "; example.com [123]", + "$TTL 864000", + "@ IN SOA ns1.linode.com. user.example.com. 2021000066 14400 14400 1209600 86400", + "@ NS ns1.linode.com.", + "@ NS ns2.linode.com.", + "@ NS ns3.linode.com.", + "@ NS ns4.linode.com.", + "@ NS ns5.linode.com.", + }, + } + + var data DataSourceModel + diags := data.parseDomainZoneFile(context.Background(), &mockZoneFileData) + assert.False(t, diags.HasError(), "Error parsing domain zone file") + + assert.Empty(t, data.DomainID, "DomainID should be 0/nil") + for _, line := range mockZoneFileData.ZoneFile { + assert.Contains(t, data.ZoneFile.String(), line, "ZoneFile content doesn't contain expected line") + } + + idJSON, _ := json.Marshal(&mockZoneFileData) + assert.Equal(t, types.StringValue(string(idJSON)), data.ID, "ID doesn't match") +} diff --git a/linode/firewall/framework_models_unit_test.go b/linode/firewall/framework_models_unit_test.go index aca5396ed..c71974768 100644 --- a/linode/firewall/framework_models_unit_test.go +++ b/linode/firewall/framework_models_unit_test.go @@ -1,22 +1,28 @@ -//go:build integration +//go:build unit package firewall import ( "context" - "github.com/linode/linodego" - "github.com/stretchr/testify/assert" "testing" "time" + + "github.com/linode/linodego" + "github.com/stretchr/testify/assert" ) // Unit tests for private functions in framework_models -// Functions under test: parseComputedAttributes, parseNonComputedAttributes, parseFirewallRules, parseFirewallLinodes +// Functions under test: parseComputedAttributes, parseNonComputedAttributes, parseFirewallRules func TestParseComputedAttributes(t *testing.T) { + createdTime := time.Date(2023, time.August, 17, 12, 0, 0, 0, time.UTC) + updatedTime := time.Date(2023, time.August, 18, 12, 0, 0, 0, time.UTC) + firewall := &linodego.Firewall{ - ID: 123, - Status: linodego.FirewallEnabled, + ID: 123, + Status: linodego.FirewallEnabled, + Created: &createdTime, + Updated: &updatedTime, } deviceEntity1 := linodego.FirewallDeviceEntity{ @@ -107,56 +113,3 @@ func TestParseNonComputedAttributes(t *testing.T) { assert.Contains(t, data.Outbound.String(), outboundRules[0].Ports) assert.Contains(t, data.Outbound.String(), (*outboundRules[0].Addresses.IPv4)[0]) } - -func TestParseFirewallLinodes(t *testing.T) { - deviceEntity1 := linodego.FirewallDeviceEntity{ - ID: 1234, - Type: linodego.FirewallDeviceLinode, - Label: "device_entity_1", - URL: "test-firewall.example.com", - } - - deviceEntity2 := linodego.FirewallDeviceEntity{ - ID: 1235, - Type: linodego.FirewallDeviceLinode, - Label: "device_entity_2", - URL: "test-firewall.example-2.com", - } - - created := time.Date(2022, time.January, 15, 12, 30, 0, 0, time.UTC) - updated := time.Date(2023, time.March, 7, 9, 45, 0, 0, time.UTC) - - device1 := linodego.FirewallDevice{ - ID: 123, - Entity: deviceEntity1, - Created: &created, - Updated: &updated, - } - - device2 := linodego.FirewallDevice{ - ID: 123, - Entity: deviceEntity2, - Created: &created, - Updated: &updated, - } - - devices := []linodego.FirewallDevice{ - device1, device2, - } - - cases := []struct { - devices []linodego.FirewallDevice - expected []int - }{ - { - devices: devices, - expected: []int{1234, 1235}, - }, - } - - for _, c := range cases { - out := parseFirewallLinodes(c.devices) - - assert.ElementsMatch(t, c.expected, out) - } -} diff --git a/linode/firewalls/datasource_test.go b/linode/firewalls/datasource_test.go index 2d3905abf..3d9b412bf 100644 --- a/linode/firewalls/datasource_test.go +++ b/linode/firewalls/datasource_test.go @@ -1,3 +1,5 @@ +//go:build integration + package firewalls_test import ( diff --git a/linode/image/framework_models_unit_test.go b/linode/image/framework_models_unit_test.go new file mode 100644 index 000000000..f5b5c1e1f --- /dev/null +++ b/linode/image/framework_models_unit_test.go @@ -0,0 +1,49 @@ +//go:build unit + +package image + +import ( + "testing" + "time" + + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/linode/linodego" + "github.com/stretchr/testify/assert" +) + +func TestParseImage(t *testing.T) { + createdTime := &time.Time{} + createdTimeFormatted := createdTime.Format(time.RFC3339) + mockImage := linodego.Image{ + ID: "linode/debian11", + CreatedBy: "linode", + Capabilities: []string{}, + Label: "Debian 11", + Description: "Example Image description.", + Type: "manual", + Vendor: "Debian", + Status: "available", + Size: 2500, + IsPublic: true, + Deprecated: false, + Created: createdTime, + Expiry: nil, + } + + var imageModel ImageModel + imageModel.ParseImage(&mockImage) + + assert.Equal(t, types.StringValue("linode/debian11"), imageModel.ID) + assert.Equal(t, types.StringValue("linode"), imageModel.CreatedBy) + assert.Empty(t, imageModel.Capabilities) + assert.Equal(t, types.StringValue("Debian 11"), imageModel.Label) + assert.Equal(t, types.StringValue("Example Image description."), imageModel.Description) + assert.Equal(t, types.StringValue("manual"), imageModel.Type) + assert.Equal(t, types.StringValue("Debian"), imageModel.Vendor) + assert.Equal(t, types.StringValue("available"), imageModel.Status) + assert.Equal(t, types.Int64Value(2500), imageModel.Size) + assert.Equal(t, types.BoolValue(true), imageModel.IsPublic) + assert.Equal(t, types.BoolValue(false), imageModel.Deprecated) + assert.Equal(t, imageModel.Created, types.StringValue(createdTimeFormatted)) + assert.Empty(t, imageModel.Expiry) +} diff --git a/linode/images/framework_models_unit_test.go b/linode/images/framework_models_unit_test.go new file mode 100644 index 000000000..036fa8f29 --- /dev/null +++ b/linode/images/framework_models_unit_test.go @@ -0,0 +1,85 @@ +//go:build unit + +package images + +import ( + "testing" + "time" + + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/linode/linodego" + "github.com/stretchr/testify/assert" +) + +func TestParseImages(t *testing.T) { + createdTime := &time.Time{} + createdTimeFormatted := createdTime.Format(time.RFC3339) + images := []linodego.Image{ + { + ID: "linode/debian11", + CreatedBy: "linode", + Capabilities: []string{}, + Label: "Debian 11", + Description: "Example Image description.", + Type: "manual", + Vendor: "Debian", + Status: "available", + Size: 2500, + IsPublic: true, + Deprecated: false, + Created: createdTime, + Expiry: nil, + }, + { + ID: "linode/debian10", + CreatedBy: "linode", + Capabilities: []string{}, + Label: "Debian 10", + Description: "Example Image description 2.", + Type: "manual", + Vendor: "Debian", + Status: "available", + Size: 2500, + IsPublic: true, + Deprecated: false, + Created: createdTime, + Expiry: nil, + }, + } + + data := ImageFilterModel{} + + data.parseImages(images) + + assert.Len(t, data.Images, len(images)) + + // Image 1 Assertions + assert.Equal(t, types.StringValue("linode/debian11"), data.Images[0].ID) + assert.Equal(t, types.StringValue("linode"), data.Images[0].CreatedBy) + assert.Empty(t, data.Images[0].Capabilities) + assert.Equal(t, types.StringValue("Debian 11"), data.Images[0].Label) + assert.Equal(t, types.StringValue("Example Image description."), data.Images[0].Description) + assert.Equal(t, types.StringValue("manual"), data.Images[0].Type) + assert.Equal(t, types.StringValue("Debian"), data.Images[0].Vendor) + assert.Equal(t, types.StringValue("available"), data.Images[0].Status) + assert.Equal(t, types.Int64Value(2500), data.Images[0].Size) + assert.Equal(t, types.BoolValue(true), data.Images[0].IsPublic) + assert.Equal(t, types.BoolValue(false), data.Images[0].Deprecated) + assert.Equal(t, data.Images[0].Created, types.StringValue(createdTimeFormatted)) + assert.Empty(t, data.Images[0].Expiry) + + // Image 2 Assertions + assert.Equal(t, types.StringValue("linode/debian10"), data.Images[1].ID) + assert.Equal(t, types.StringValue("linode"), data.Images[1].CreatedBy) + assert.Empty(t, data.Images[0].Capabilities) + assert.Equal(t, types.StringValue("Debian 10"), data.Images[1].Label) + assert.Equal(t, types.StringValue("Example Image description 2."), data.Images[1].Description) + assert.Equal(t, types.StringValue("manual"), data.Images[1].Type) + assert.Equal(t, types.StringValue("Debian"), data.Images[1].Vendor) + assert.Equal(t, types.StringValue("available"), data.Images[1].Status) + assert.Equal(t, types.Int64Value(2500), data.Images[1].Size) + assert.Equal(t, types.BoolValue(true), data.Images[1].IsPublic) + assert.Equal(t, types.BoolValue(false), data.Images[1].Deprecated) + assert.Equal(t, data.Images[1].Created, types.StringValue(createdTimeFormatted)) + assert.Empty(t, data.Images[1].Expiry) +} diff --git a/linode/instance/expand_unit_test.go b/linode/instance/expand_unit_test.go new file mode 100644 index 000000000..5cd1d3a6b --- /dev/null +++ b/linode/instance/expand_unit_test.go @@ -0,0 +1,134 @@ +//go:build unit + +package instance + +import ( + "github.com/linode/linodego" + "testing" +) + +func TestExpandInstanceConfigDeviceMap(t *testing.T) { + deviceMapInput := map[string]interface{}{ + "sda": []interface{}{ + map[string]interface{}{ + "disk_id": 124458, + "volume_id": nil, + }, + }, + "sdb": []interface{}{ + map[string]interface{}{ + "disk_id": 124459, + "volume_id": nil, + }, + }, + } + + diskIDLabelMap := map[string]int{ + "example_label_sda": 124458, + "example_label_sdb": 124459, + } + + deviceMap, err := expandInstanceConfigDeviceMap(deviceMapInput, diskIDLabelMap) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + if deviceMap == nil { + t.Error("Expected deviceMap to not be nil") + } + + // Assert the DiskID for SDA and SDB + expectedDiskID_SDA := 124458 + if deviceMap.SDA.DiskID != expectedDiskID_SDA { + t.Errorf("Expected DiskID %d for SDA, but got %d", expectedDiskID_SDA, deviceMap.SDA.DiskID) + } + + expectedDiskID_SDB := 124459 + if deviceMap.SDB.DiskID != expectedDiskID_SDB { + t.Errorf("Expected DiskID %d for SDB, but got %d", expectedDiskID_SDB, deviceMap.SDB.DiskID) + } + + // Assert that other device slots are nil + if deviceMap.SDC != nil || deviceMap.SDD != nil || deviceMap.SDE != nil || + deviceMap.SDF != nil || deviceMap.SDG != nil || deviceMap.SDH != nil { + t.Errorf("Expected all other device slots to be nil") + } +} + +func TestExpandInstanceConfigDevice(t *testing.T) { + tests := []struct { + name string + m map[string]interface{} + want *linodego.InstanceConfigDevice + }{ + { + name: "Valid DiskID", + m: map[string]interface{}{ + "disk_id": 123, + }, + want: &linodego.InstanceConfigDevice{ + DiskID: 123, + }, + }, + { + name: "Valid VolumeID", + m: map[string]interface{}{ + "volume_id": 456, + }, + want: &linodego.InstanceConfigDevice{ + VolumeID: 456, + }, + }, + { + name: "Invalid IDs", + m: map[string]interface{}{ + "disk_id": 0, + "volume_id": -1, + }, + want: nil, + }, + { + name: "No IDs", + m: map[string]interface{}{}, + want: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := expandInstanceConfigDevice(tt.m) + if got != nil && tt.want == nil || got == nil && tt.want != nil { + t.Errorf("expandInstanceConfigDevice() = %v, want %v", got, tt.want) + } else if got != nil && tt.want != nil { + if got.DiskID != tt.want.DiskID || got.VolumeID != tt.want.VolumeID { + t.Errorf("expandInstanceConfigDevice() = %v, want %v", got, tt.want) + } + } + }) + } +} + +func TestExpandConfigInterface(t *testing.T) { + interfaceInput := map[string]interface{}{ + "label": "eth0.100", + "purpose": "vlan", + "ipam_address": "192.168.1.2/24", + } + + interfaceResult := expandConfigInterface(interfaceInput) + + expectedLabel := "eth0.100" + if interfaceResult.Label != expectedLabel { + t.Errorf("Expected label %s, but got %s", expectedLabel, interfaceResult.Label) + } + + expectedPurpose := linodego.InterfacePurposeVLAN + if interfaceResult.Purpose != expectedPurpose { + t.Errorf("Expected purpose %s, but got %s", expectedPurpose, interfaceResult.Purpose) + } + + expectedIPAMAddress := "192.168.1.2/24" + if interfaceResult.IPAMAddress != expectedIPAMAddress { + t.Errorf("Expected IPAMAddress %s, but got %s", expectedIPAMAddress, interfaceResult.IPAMAddress) + } +} diff --git a/linode/instance/flatten_unit_test.go b/linode/instance/flatten_unit_test.go new file mode 100644 index 000000000..6f5eef03f --- /dev/null +++ b/linode/instance/flatten_unit_test.go @@ -0,0 +1,398 @@ +//go:build unit + +package instance + +import ( + "github.com/linode/linodego" + "reflect" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +// Test helper functions +func isEqual(a, b []map[string]interface{}) bool { + if len(a) != len(b) { + return false + } + + for i := range a { + for key := range a[i] { + if a[i][key] != b[i][key] { + return false + } + } + } + + return true +} + +func areMapsEqual(a, b map[string]interface{}) bool { + return reflect.DeepEqual(a, b) +} + +// Unit tests for functions in flatten.go +func TestFlattenInstanceAlerts(t *testing.T) { + instance := linodego.Instance{ + ID: 123, + Created: &time.Time{}, + Updated: &time.Time{}, + Region: "us-east", + Alerts: &linodego.InstanceAlert{ + CPU: 180, + IO: 10000, + NetworkIn: 10, + NetworkOut: 10, + TransferQuota: 80, + }, + Backups: &linodego.InstanceBackup{ + Available: true, + Enabled: true, + }, + Image: "linode/debian10", + Group: "Linode-Group", + IPv6: "c001:d00d::1337/128", + Label: "linode123", + Type: "g6-standard-1", + Status: linodego.InstanceStatus("running"), + HasUserData: false, + Hypervisor: "kvm", + HostUUID: "3a3ddd59d9a78bb8de041391075df44de62bfec8", + Specs: &linodego.InstanceSpec{ + Disk: 81920, + GPUs: 0, + Memory: 4096, + Transfer: 4000, + VCPUs: 2, + }, + WatchdogEnabled: true, + Tags: []string{"example tag", "another example"}, + } + alerts := flattenInstanceAlerts(instance) + + expectedAlerts := []map[string]int{ + { + "cpu": 180, + "io": 10000, + "network_in": 10, + "network_out": 10, + "transfer_quota": 80, + }, + } + + assert.Equal(t, expectedAlerts, alerts) +} + +func TestFlattenInstanceBackups(t *testing.T) { + instance := linodego.Instance{ + ID: 123, + Created: &time.Time{}, + Updated: &time.Time{}, + Region: "us-east", + Backups: &linodego.InstanceBackup{ + Available: true, + Enabled: true, + Schedule: struct { + Day string `json:"day,omitempty"` + Window string `json:"window,omitempty"` + }{ + Day: "Saturday", + Window: "W22", + }, + }, + } + + backups := flattenInstanceBackups(instance) + + expectedBackups := []map[string]interface{}{ + { + "available": true, + "enabled": true, + "schedule": []map[string]interface{}{ + { + "day": "Saturday", + "window": "W22", + }, + }, + }, + } + + assert.Equal(t, expectedBackups, backups) +} + +func TestFlattenInstanceDisks(t *testing.T) { + instanceDisks := []linodego.InstanceDisk{ + { + ID: 25674, + Label: "Debian 9 Disk", + Status: "ready", + Size: 48640, + Filesystem: "ext4", + Created: nil, + Updated: nil, + }, + } + + disks, swapSize := flattenInstanceDisks(instanceDisks) + + expectedDisks := []map[string]interface{}{ + { + "id": 25674, + "size": 48640, + "label": "Debian 9 Disk", + "filesystem": "ext4", + }, + } + + assert.Equal(t, expectedDisks, disks, "Flattened disks do not match expected") + assert.Equal(t, 0, swapSize, "Swap size does not match expected") +} + +func TestFlattenInstanceConfigDevice(t *testing.T) { + diskLabelIDMap := map[int]string{ + 1: "DiskLabel1", + 2: "DiskLabel2", + } + + deviceWithDisk := &linodego.InstanceConfigDevice{ + DiskID: 1, + } + resultDisk := flattenInstanceConfigDevice(deviceWithDisk, diskLabelIDMap) + expectedResultDisk := []map[string]interface{}{ + { + "disk_id": 1, + "disk_label": "DiskLabel1", + }, + } + if !isEqual(resultDisk, expectedResultDisk) { + t.Errorf("Expected %v, but got %v", expectedResultDisk, resultDisk) + } + + deviceWithVolume := &linodego.InstanceConfigDevice{ + VolumeID: 3, + } + resultVolume := flattenInstanceConfigDevice(deviceWithVolume, diskLabelIDMap) + expectedResultVolume := []map[string]interface{}{ + { + "volume_id": 3, + }, + } + if !isEqual(resultVolume, expectedResultVolume) { + t.Errorf("Expected %v, but got %v", expectedResultVolume, resultVolume) + } + + emptyDevice := &linodego.InstanceConfigDevice{} + resultEmpty := flattenInstanceConfigDevice(emptyDevice, diskLabelIDMap) + if resultEmpty != nil { + t.Errorf("Expected nil for an empty device, but got %v", resultEmpty) + } +} + +func TestFlattenInstanceConfigs(t *testing.T) { + diskLabelIDMap := map[int]string{ + 124458: "disk_label", + } + + instanceConfigs := []linodego.InstanceConfig{ + { + ID: 1, + Label: "config1", + Comments: "test config", + Devices: &linodego.InstanceConfigDeviceMap{ + SDA: &linodego.InstanceConfigDevice{ + DiskID: 124458, + VolumeID: 1, + }, + SDB: &linodego.InstanceConfigDevice{ + DiskID: 124458, + VolumeID: 2, + }, + SDC: &linodego.InstanceConfigDevice{ + DiskID: 124458, + VolumeID: 3, + }, + SDD: &linodego.InstanceConfigDevice{ + DiskID: 124458, + VolumeID: 4, + }, + SDE: &linodego.InstanceConfigDevice{ + DiskID: 124458, + VolumeID: 5, + }, + SDF: &linodego.InstanceConfigDevice{ + DiskID: 124458, + VolumeID: 6, + }, + SDG: &linodego.InstanceConfigDevice{ + DiskID: 124458, + VolumeID: 7, + }, + SDH: &linodego.InstanceConfigDevice{ + DiskID: 124458, + VolumeID: 8, + }, + }, + Helpers: &linodego.InstanceConfigHelpers{}, + Interfaces: []linodego.InstanceConfigInterface{}, + MemoryLimit: 2048, + Kernel: "linode/latest-64bit", + RootDevice: "/dev/sda", + RunLevel: "default", + VirtMode: "paravirt", + }, + } + + expectedConfigs := []map[string]interface{}{ + { + "root_device": "/dev/sda", + "kernel": "linode/latest-64bit", + "run_level": "default", + "virt_mode": "paravirt", + "comments": "test config", + "memory_limit": 2048, + "label": "config1", + "helpers": []map[string]bool{ + { + "updatedb_disabled": false, + "distro": false, + "modules_dep": false, + "network": false, + "devtmpfs_automount": false, + }, + }, + "devices": []map[string]interface{}{ + { + "sda": []map[string]interface{}{ + { + "disk_id": 124458, + "disk_label": "disk_label", + }, + }, + "sdb": []map[string]interface{}{ + { + "disk_id": 124458, + "disk_label": "disk_label", + }, + }, + "sdc": []map[string]interface{}{ + { + "disk_id": 124458, + "disk_label": "disk_label", + }, + }, + "sdd": []map[string]interface{}{ + { + "disk_id": 124458, + "disk_label": "disk_label", + }, + }, + "sde": []map[string]interface{}{ + { + "disk_id": 124458, + "disk_label": "disk_label", + }, + }, + "sdf": []map[string]interface{}{ + { + "disk_id": 124458, + "disk_label": "disk_label", + }, + }, + "sdg": []map[string]interface{}{ + { + "disk_id": 124458, + "disk_label": "disk_label", + }, + }, + "sdh": []map[string]interface{}{ + { + "disk_id": 124458, + "disk_label": "disk_label", + }, + }, + }, + }, + "interface": []interface{}{}, + }, + } + + actualConfigs := flattenInstanceConfigs(instanceConfigs, diskLabelIDMap) + + if len(actualConfigs) != len(expectedConfigs) { + t.Fatalf("Expected %d configs, but got %d", len(expectedConfigs), len(actualConfigs)) + } + + for i := 0; i < len(expectedConfigs); i++ { + + if !areMapsEqual(actualConfigs[i], expectedConfigs[i]) { + t.Errorf("Config %d: Mismatch between expected and actual config", i) + } + } +} + +func TestFlattenConfigInterface(t *testing.T) { + configInterface := linodego.InstanceConfigInterface{ + IPAMAddress: "192.168.1.1", + Label: "test-vlan", + Purpose: linodego.InterfacePurposeVLAN, + } + + result := flattenConfigInterface(configInterface) + + expected := map[string]interface{}{ + "ipam_address": "192.168.1.1", + "label": "test-vlan", + "purpose": linodego.InterfacePurposeVLAN, + } + + if len(result) != len(expected) { + t.Errorf("Result map length does not match the expected map length") + } + + for key, expectedValue := range expected { + if resultValue, ok := result[key]; !ok || resultValue != expectedValue { + t.Errorf("Mismatch for key %s: Expected %v, but got %v", key, expectedValue, resultValue) + } + } +} + +func TestFlattenInstanceSpecs(t *testing.T) { + instance := linodego.Instance{ + ID: 1, + Region: "us-east", + Image: "linode/debian10", + Label: "test-instance", + Type: "g6-standard-1", + Status: linodego.InstanceRunning, + Specs: &linodego.InstanceSpec{ + VCPUs: 2, + Disk: 50, + Memory: 4096, + Transfer: 2000, + }, + } + + result := flattenInstanceSpecs(instance) + + expected := []map[string]int{ + { + "vcpus": 2, + "disk": 50, + "memory": 4096, + "transfer": 2000, + }, + } + + if len(result) != len(expected) { + t.Errorf("Result slice length does not match the expected slice length") + } + + for i := 0; i < len(result); i++ { + for key, expectedValue := range expected[i] { + if resultValue, ok := result[i][key]; !ok || resultValue != expectedValue { + t.Errorf("Mismatch for key %s: Expected %d, but got %d", key, expectedValue, resultValue) + } + } + } +} diff --git a/linode/instancenetworking/framework_models_unit_test.go b/linode/instancenetworking/framework_models_unit_test.go new file mode 100644 index 000000000..617bcc781 --- /dev/null +++ b/linode/instancenetworking/framework_models_unit_test.go @@ -0,0 +1,51 @@ +//go:build unit + +package instancenetworking + +import ( + "context" + "testing" + + "github.com/linode/linodego" + "github.com/stretchr/testify/assert" +) + +func TestParseInstanceIPAddressResponse(t *testing.T) { + instanceIPResponse := &linodego.InstanceIPAddressResponse{ + IPv4: &linodego.InstanceIPv4Response{ + Public: []*linodego.InstanceIP{ + { + Address: "1.2.3.4", + Type: "ipv4", + Public: true, + }, + }, + Private: []*linodego.InstanceIP{ + { + Address: "10.0.0.1", + Type: "ipv4", + Public: false, + }, + }, + }, + IPv6: &linodego.InstanceIPv6Response{ + LinkLocal: &linodego.InstanceIP{ + Address: "fe80::1", + Type: "ipv6", + }, + SLAAC: &linodego.InstanceIP{ + Address: "fe80::1", + Type: "ipv6", + }, + }, + } + + dataSourceModel := &DataSourceModel{} + + diags := dataSourceModel.parseInstanceIPAddressResponse(context.Background(), instanceIPResponse) + + assert.False(t, diags.HasError()) + + assert.Contains(t, dataSourceModel.IPV4.String(), "1.2.3.4") + assert.Contains(t, dataSourceModel.IPV6.String(), "fe80::1") +} diff --git a/linode/instancetype/framework_models_unit_test.go b/linode/instancetype/framework_models_unit_test.go new file mode 100644 index 000000000..139da18ff --- /dev/null +++ b/linode/instancetype/framework_models_unit_test.go @@ -0,0 +1,53 @@ +//go:build unit + +package instancetype + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/linode/linodego" + "github.com/stretchr/testify/assert" +) + +func TestParseLinodeType(t *testing.T) { + mockLinodeType := &linodego.LinodeType{ + ID: "g6-standard-2", + Disk: 81920, + Class: linodego.ClassStandard, + Label: "Linode 4GB", + NetworkOut: 1000, + Memory: 4096, + Transfer: 4000, + VCPUs: 2, + GPUs: 0, + Successor: "", + Price: &linodego.LinodePrice{ + Hourly: 0.03, + Monthly: 20, + }, + Addons: &linodego.LinodeAddons{ + Backups: &linodego.LinodeBackupsAddon{ + Price: &linodego.LinodePrice{ + Hourly: 0.008, + Monthly: 5, + }, + }, + }, + } + + data := &DataSourceModel{} + + diags := data.ParseLinodeType(context.Background(), mockLinodeType) + assert.False(t, diags.HasError(), "Unexpected error") + + assert.Equal(t, types.StringValue("g6-standard-2"), data.ID) + assert.Equal(t, types.Int64Value(81920), data.Disk) + assert.Equal(t, types.StringValue("standard"), data.Class) + assert.Equal(t, types.StringValue("Linode 4GB"), data.Label) + assert.Equal(t, types.Int64Value(1000), data.NetworkOut) + assert.Equal(t, types.Int64Value(4096), data.Memory) + assert.Equal(t, types.Int64Value(4000), data.Transfer) + assert.Equal(t, types.Int64Value(2), data.VCPUs) +} diff --git a/linode/instancetypes/framework_models_unit_test.go b/linode/instancetypes/framework_models_unit_test.go new file mode 100644 index 000000000..1a872676a --- /dev/null +++ b/linode/instancetypes/framework_models_unit_test.go @@ -0,0 +1,94 @@ +//go:build unit + +package instancetypes + +import ( + "context" + "strconv" + "testing" + + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/linode/linodego" + "github.com/stretchr/testify/assert" +) + +func TestParseInstanceTypes(t *testing.T) { + mockTypes := []linodego.LinodeType{ + { + ID: "g6-standard-2", + Disk: 81920, + Class: linodego.ClassStandard, + Label: "Linode 4GB", + NetworkOut: 1000, + Memory: 4096, + Transfer: 4000, + VCPUs: 2, + GPUs: 0, + Successor: "", + Price: &linodego.LinodePrice{ + Hourly: 0.03, + Monthly: 20, + }, + Addons: &linodego.LinodeAddons{ + Backups: &linodego.LinodeBackupsAddon{ + Price: &linodego.LinodePrice{ + Hourly: 0.008, + Monthly: 5, + }, + }, + }, + }, + { + ID: "g2-nanode-1", + Disk: 81920, + Class: linodego.ClassNanode, + Label: "Linode 4GB", + NetworkOut: 1000, + Memory: 2048, + Transfer: 4000, + VCPUs: 2, + GPUs: 0, + Successor: "", + Price: &linodego.LinodePrice{ + Hourly: 0.01, + Monthly: 10, + }, + Addons: &linodego.LinodeAddons{ + Backups: &linodego.LinodeBackupsAddon{ + Price: &linodego.LinodePrice{ + Hourly: 0.008, + Monthly: 5, + }, + }, + }, + }, + } + + model := &InstanceTypeFilterModel{} + + diags := model.parseInstanceTypes(context.Background(), mockTypes) + assert.False(t, diags.HasError(), "Unexpected error") + + assert.Len(t, model.Types, len(mockTypes), "Number of parsed types does not match") + + // Assertions for each mock instance type + for i, mockType := range mockTypes { + assert.Equal(t, model.Types[i].ID, types.StringValue(mockType.ID), "ID doesn't match") + assert.Equal(t, model.Types[i].Disk, types.Int64Value(int64(mockType.Disk)), "Disk size doesn't match") + assert.Equal(t, model.Types[i].Class, types.StringValue(string(mockType.Class)), "Class doesn't match") + assert.Equal(t, model.Types[i].Label, types.StringValue(mockType.Label), "Label doesn't match") + assert.Equal(t, model.Types[i].NetworkOut, types.Int64Value(int64(mockType.NetworkOut)), "NetworkOut doesn't match") + assert.Equal(t, model.Types[i].Memory, types.Int64Value(int64(mockType.Memory)), "Memory size doesn't match") + assert.Equal(t, model.Types[i].Transfer, types.Int64Value(int64(mockType.Transfer)), "Transfer size doesn't match") + assert.Equal(t, model.Types[i].VCPUs, types.Int64Value(int64(mockType.VCPUs)), "VCPUs count doesn't match") + + // Assertions for Price + assert.NotNil(t, model.Types[i].Price, "Price should not be nil") + assert.Contains(t, model.Types[i].Price.String(), strconv.FormatFloat(float64(mockType.Price.Hourly), 'f', -1, 32), "Hourly price doesn't match") + assert.Contains(t, model.Types[i].Price.String(), strconv.FormatFloat(float64(mockType.Price.Monthly), 'f', -1, 32), "Monthly price doesn't match") + + // Assertions for Addons + assert.Contains(t, model.Types[i].Addons.String(), strconv.FormatFloat(float64(mockType.Addons.Backups.Price.Hourly), 'f', -1, 32), "Backups hourly price doesn't match") + assert.Contains(t, model.Types[i].Addons.String(), strconv.FormatFloat(float64(mockType.Addons.Backups.Price.Monthly), 'f', -1, 32), "Backups monthly price doesn't match") + } +} diff --git a/linode/ipv6range/framework_models_unit_test.go b/linode/ipv6range/framework_models_unit_test.go new file mode 100644 index 000000000..8e4218b29 --- /dev/null +++ b/linode/ipv6range/framework_models_unit_test.go @@ -0,0 +1,52 @@ +//go:build unit + +package ipv6range + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/linode/linodego" + "github.com/stretchr/testify/assert" +) + +func TestParseIPv6RangeDataSource(t *testing.T) { + ipRange := linodego.IPv6Range{ + Range: "2600:3c01::", + Region: "us-east", + Prefix: 64, + RouteTarget: "2600:3c01::ffff:ffff:ffff:ffff", + IsBGP: true, + Linodes: []int{123, 456}, + } + + dataSourceModel := DataSourceModel{} + diags := dataSourceModel.parseIPv6RangeDataSource(context.Background(), &ipRange) + + assert.Nil(t, diags) + assert.Equal(t, types.StringValue("2600:3c01::"), dataSourceModel.Range) + assert.Equal(t, dataSourceModel.IsBGP, types.BoolValue(true)) + assert.Equal(t, types.Int64Value(64), dataSourceModel.Prefix) + assert.Equal(t, types.StringValue("us-east"), dataSourceModel.Region) + assert.NotEmpty(t, dataSourceModel.ID) +} + +func TestParseIPv6RangeResourceDataComputedAttrs(t *testing.T) { + ipRange := linodego.IPv6Range{ + Range: "2600:3c01::", + Region: "us-east", + Prefix: 64, + RouteTarget: "2600:3c01::ffff:ffff:ffff:ffff", + IsBGP: false, + Linodes: []int{789}, + } + + resourceModel := ResourceModel{} + diags := resourceModel.parseIPv6RangeResourceDataComputedAttrs(context.Background(), &ipRange) + + assert.Nil(t, diags) + assert.Equal(t, types.StringValue("2600:3c01::"), resourceModel.Range) + assert.Equal(t, resourceModel.IsBGP, types.BoolValue(false)) + assert.Equal(t, types.StringValue("us-east"), resourceModel.Region) +} diff --git a/linode/kernel/framework_models_unit_test.go b/linode/kernel/framework_models_unit_test.go new file mode 100644 index 000000000..25158436b --- /dev/null +++ b/linode/kernel/framework_models_unit_test.go @@ -0,0 +1,38 @@ +//go:build unit + +package kernel + +import ( + "testing" + "time" + + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/linode/linodego" + "github.com/stretchr/testify/assert" +) + +func TestParseKernel(t *testing.T) { + testKernel := linodego.LinodeKernel{ + ID: "linode/latest-64bit", + Label: "Latest 64 bit (4.15.7-x86_64-linode102)", + Version: "4.15.7", + Architecture: "x86_64", + Deprecated: false, + KVM: true, + XEN: false, + PVOPS: false, + Built: &time.Time{}, + } + + var data DataSourceModel + data.ParseKernel(nil, &testKernel) + + assert.Equal(t, types.StringValue("linode/latest-64bit"), data.ID) + assert.Equal(t, types.StringValue("x86_64"), data.Architecture) + assert.Equal(t, types.BoolValue(false), data.Deprecated) + assert.Equal(t, types.BoolValue(true), data.KVM) + assert.Equal(t, types.StringValue("Latest 64 bit (4.15.7-x86_64-linode102)"), data.Label) + assert.Equal(t, types.BoolValue(false), data.PVOPS) + assert.Equal(t, types.StringValue("4.15.7"), data.Version) + assert.Equal(t, types.BoolValue(false), data.XEN) +} diff --git a/linode/kernels/datasource_test.go b/linode/kernels/datasource_test.go index 000675c2b..58836c49c 100644 --- a/linode/kernels/datasource_test.go +++ b/linode/kernels/datasource_test.go @@ -1,3 +1,5 @@ +//go:build integration + package kernels_test import ( diff --git a/linode/kernels/framework_models_unit_test.go b/linode/kernels/framework_models_unit_test.go new file mode 100644 index 000000000..eeeb31538 --- /dev/null +++ b/linode/kernels/framework_models_unit_test.go @@ -0,0 +1,37 @@ +//go:build unit + +package kernels + +import ( + "context" + "testing" + "time" + + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/linode/linodego" + "github.com/stretchr/testify/assert" +) + +func TestParseKernels(t *testing.T) { + kernels := []linodego.LinodeKernel{ + { + ID: "linode/latest-64bit", + Label: "Latest 64 bit (4.15.7-x86_64-linode102)", + Version: "4.15.7", + Architecture: "x86_64", + Deprecated: false, + KVM: true, + XEN: false, + PVOPS: false, + Built: &time.Time{}, + }, + } + + var filterModel KernelFilterModel + filterModel.parseKernels(context.Background(), kernels) + + assert.Len(t, filterModel.Kernels, len(kernels)) + + assert.Equal(t, types.StringValue("linode/latest-64bit"), filterModel.Kernels[0].ID) + assert.Equal(t, types.StringValue("x86_64"), filterModel.Kernels[0].Architecture) +} diff --git a/linode/nb/framework_models_unit_test.go b/linode/nb/framework_models_unit_test.go new file mode 100644 index 000000000..13b011f75 --- /dev/null +++ b/linode/nb/framework_models_unit_test.go @@ -0,0 +1,109 @@ +//go:build unit + +package nb + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/linode/linodego" + "github.com/stretchr/testify/assert" +) + +func TestParseNonComputedAttrs(t *testing.T) { + label := "test-nodebalancer" + + nodeBalancer := &linodego.NodeBalancer{ + ID: 123, + Label: &label, + Tags: []string{"tag1", "tag2"}, + } + + nodeBalancerModel := &NodebalancerModel{} + + diags := nodeBalancerModel.ParseNonComputedAttrs(context.Background(), nodeBalancer) + + assert.False(t, diags.HasError(), "Errors should be returned due to custom context error") + assert.Equal(t, types.StringValue("test-nodebalancer"), nodeBalancerModel.Label) + assert.Contains(t, nodeBalancer.Tags, "tag1") + assert.Contains(t, nodeBalancer.Tags, "tag2") +} + +func TestParseComputedAttrs(t *testing.T) { + hostname := "example.nodebalancer.linode.com" + IPv4 := "192.168.1.1" + IPv6 := "2001:db8::1" + + createdTime := time.Date(2023, time.August, 17, 12, 0, 0, 0, time.UTC) + updatedTime := time.Date(2023, time.August, 17, 14, 0, 0, 0, time.UTC) + + transferIn := float64(100.0) + transferOut := float64(200.0) + transferTotal := float64(300.0) + + nodeBalancer := &linodego.NodeBalancer{ + ID: 123, + Region: "us-east", + ClientConnThrottle: 10, + Hostname: &hostname, + IPv4: &IPv4, + IPv6: &IPv6, + Created: &createdTime, + Updated: &updatedTime, + Transfer: linodego.NodeBalancerTransfer{ + In: &transferIn, + Out: &transferOut, + Total: &transferTotal, + }, + } + + nodeBalancerModel := &NodebalancerModel{} + + diags := nodeBalancerModel.ParseComputedAttrs(context.Background(), nodeBalancer) + + assert.False(t, diags.HasError()) + + assert.Equal(t, types.Int64Value(123), nodeBalancerModel.ID) + assert.Equal(t, types.StringValue("us-east"), nodeBalancerModel.Region) + assert.Equal(t, types.Int64Value(10), nodeBalancerModel.ClientConnThrottle) + assert.Equal(t, types.StringPointerValue(&hostname), nodeBalancerModel.Hostname) + assert.Equal(t, types.StringPointerValue(&IPv4), nodeBalancerModel.Ipv4) + assert.Equal(t, types.StringPointerValue(&IPv6), nodeBalancerModel.Ipv6) + + assert.NotNil(t, nodeBalancerModel.Created) + assert.NotNil(t, nodeBalancerModel.Updated) + + assert.Contains(t, nodeBalancerModel.Transfer.String(), "100.0") + assert.Contains(t, nodeBalancerModel.Transfer.String(), "200.0") + assert.Contains(t, nodeBalancerModel.Transfer.String(), "300.0") +} + +func TestUpgradeResourceStateValue(t *testing.T) { + t.Run("ValidFloatConversion", func(t *testing.T) { + value := "42.5" + result, diag := UpgradeResourceStateValue(value) + + assert.Empty(t, diag) + assert.Equal(t, "42.500000", result.String()) + }) + + t.Run("EmptyValue", func(t *testing.T) { + value := "" + result, diag := UpgradeResourceStateValue(value) + + assert.Empty(t, diag) + assert.Equal(t, "0.000000", result.String()) + }) + + t.Run("InvalidFloatConversion", func(t *testing.T) { + value := "invalid" + result, diag := UpgradeResourceStateValue(value) + + fmt.Println(diag.Detail()) + assert.Contains(t, diag.Detail(), "strconv.ParseFloat: parsing \"invalid\": invalid syntax") + assert.Empty(t, result) + }) +} diff --git a/linode/nbnode/framework_models_unit_test.go b/linode/nbnode/framework_models_unit_test.go new file mode 100644 index 000000000..383c45249 --- /dev/null +++ b/linode/nbnode/framework_models_unit_test.go @@ -0,0 +1,37 @@ +//go:build unit + +package nbnode + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/linode/linodego" + "github.com/stretchr/testify/assert" +) + +func TestParseNodeBalancerNode(t *testing.T) { + mockNode := &linodego.NodeBalancerNode{ + ID: 54321, + Address: "192.168.210.120:80", + Label: "node54321", + Status: "UP", + Weight: 50, + Mode: "accept", + ConfigID: 4567, + NodeBalancerID: 12345, + } + + data := &DataSourceModel{} + + data.ParseNodeBalancerNode(mockNode) + + assert.Equal(t, types.Int64Value(54321), data.ID) + assert.Equal(t, types.Int64Value(12345), data.NodeBalancerID) + assert.Equal(t, types.Int64Value(4567), data.ConfigID) + assert.Equal(t, types.StringValue("node54321"), data.Label) + assert.Equal(t, types.Int64Value(50), data.Weight) + assert.Equal(t, types.StringValue("accept"), data.Mode) + assert.Equal(t, types.StringValue("192.168.210.120:80"), data.Address) + assert.Equal(t, types.StringValue("UP"), data.Status) +} diff --git a/linode/nbs/datasource_test.go b/linode/nbs/datasource_test.go index c6c003d77..2fef42a55 100644 --- a/linode/nbs/datasource_test.go +++ b/linode/nbs/datasource_test.go @@ -1,3 +1,5 @@ +//go:build integration + package nbs_test import ( diff --git a/linode/objkey/framework_models_unit_test.go b/linode/objkey/framework_models_unit_test.go new file mode 100644 index 000000000..bc01fccd3 --- /dev/null +++ b/linode/objkey/framework_models_unit_test.go @@ -0,0 +1,62 @@ +//go:build unit + +package objkey + +import ( + "testing" + + "github.com/linode/linodego" +) + +func TestParseConfiguredAttributes(t *testing.T) { + bucketAccessData := []linodego.ObjectStorageKeyBucketAccess{ + { + Cluster: "ap-south-1", + BucketName: "example-bucket", + Permissions: "read_only", + }, + } + + key := linodego.ObjectStorageKey{ + ID: 123, + Label: "my-key", + AccessKey: "KVAKUTGBA4WTR2NSJQ81", + SecretKey: "OiA6F5r0niLs3QA2stbyq7mY5VCV7KqOzcmitmHw", + Limited: true, + BucketAccess: &bucketAccessData, + } + + data := ResourceModel{} + data.parseConfiguredAttributes(&key) + //assert.Equal(t, types.Int64Value(123), data.ID) + //assert.Equal(t, types.StringValue("my-key"), data.Label) + //assert.Equal(t, types.StringValue("KVAKUTGBA4WTR2NSJQ81"), data.AccessKey) + //assert.Equal(t, types.StringValue("OiA6F5r0niLs3QA2stbyq7mY5VCV7KqOzcmitmHw"), data.SecretKey) + //assert.Equal(t, types.BoolValue(true), data.Limited) + + //assert.NotNil(t, data.BucketAccess) + // + //bucketAccessEntry := data.BucketAccess[0] + //assert.Equal(t, types.StringValue("ap-south-1"), bucketAccessEntry.Cluster) + //assert.Equal(t, types.StringValue("example-bucket"), bucketAccessEntry.BucketName) + //assert.Equal(t, types.StringValue("read_only"), bucketAccessEntry.Permissions) +} + +func TestParseComputedAttributes(t *testing.T) { + key := linodego.ObjectStorageKey{ + ID: 123, + AccessKey: "KVAKUTGBA4WTR2NSJQ81", + SecretKey: "[REDACTED]", + Limited: true, + BucketAccess: nil, + } + + rm := ResourceModel{} + rm.parseComputedAttributes(&key) + + //assert.Equal(t, types.Int64Value(123), rm.ID) + //assert.Equal(t, types.StringValue("KVAKUTGBA4WTR2NSJQ81"), rm.AccessKey) + //assert.Equal(t, rm.Limited, types.BoolValue(true)) + // + //assert.Equal(t, types.StringValue(""), rm.SecretKey) +} diff --git a/linode/rdns/framework_models_unit_test.go b/linode/rdns/framework_models_unit_test.go new file mode 100644 index 000000000..2291deaf3 --- /dev/null +++ b/linode/rdns/framework_models_unit_test.go @@ -0,0 +1,37 @@ +//go:build unit + +package rdns + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/linode/linodego" + "github.com/stretchr/testify/assert" +) + +func TestParseConfiguredAttributes(t *testing.T) { + ip := &linodego.InstanceIP{ + Address: "192.168.1.1", + RDNS: "linode.example.com", + } + + rm := &ResourceModel{} + + rm.parseConfiguredAttributes(ip) + + assert.Equal(t, types.StringValue("192.168.1.1"), rm.Address) + assert.Equal(t, types.StringValue("linode.example.com"), rm.RDNS) +} + +func TestParseComputedAttributes(t *testing.T) { + ip := &linodego.InstanceIP{ + Address: "192.168.1.1", + } + + rm := &ResourceModel{} + + rm.parseComputedAttributes(ip) + + assert.Equal(t, types.StringValue("192.168.1.1"), rm.ID) +} diff --git a/linode/region/framework_models_unit_test.go b/linode/region/framework_models_unit_test.go new file mode 100644 index 000000000..9847df28c --- /dev/null +++ b/linode/region/framework_models_unit_test.go @@ -0,0 +1,41 @@ +//go:build unit + +package region + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/linode/linodego" + "github.com/stretchr/testify/assert" +) + +func TestParseRegion(t *testing.T) { + region := &linodego.Region{ + ID: "us-east", + Country: "us", + Capabilities: []string{"Linodes", "NodeBalancers", "Block Storage", "Object Storage"}, + Status: "ok", + Resolvers: linodego.RegionResolvers{ + IPv4: "192.0.2.0,192.0.2.1", + IPv6: "2001:0db8::,2001:0db8::1", + }, + Label: "Newark, NJ, USA", + } + + model := &RegionModel{} + + model.parseRegion(region) + + assert.Equal(t, types.StringValue("us-east"), model.ID) + assert.Equal(t, types.StringValue("Newark, NJ, USA"), model.Label) + assert.Equal(t, types.StringValue("ok"), model.Status) + assert.Equal(t, types.StringValue("us"), model.Country) + + for i, capability := range region.Capabilities { + assert.Equal(t, types.StringValue(capability), model.Capabilities[i]) + } + + assert.Equal(t, types.StringValue("192.0.2.0,192.0.2.1"), model.Resolvers[0].IPv4) + assert.Equal(t, types.StringValue("2001:0db8::,2001:0db8::1"), model.Resolvers[0].IPv6) +} diff --git a/linode/regions/framework_models_unit_test.go b/linode/regions/framework_models_unit_test.go new file mode 100644 index 000000000..77b6b4e08 --- /dev/null +++ b/linode/regions/framework_models_unit_test.go @@ -0,0 +1,56 @@ +//go:build unit + +package regions + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/linode/linodego" + "github.com/stretchr/testify/assert" +) + +func TestParseRegions(t *testing.T) { + regions := []linodego.Region{ + { + ID: "us-east", + Label: "Newark, NJ, USA", + Status: "ok", + Country: "us", + Capabilities: []string{"Linodes", "NodeBalancers", "Block Storage", "Object Storage"}, + Resolvers: linodego.RegionResolvers{ + IPv4: "192.0.2.0,192.0.2.1", + IPv6: "2001:0db8::,2001:0db8::1", + }, + }, + { + ID: "ap-west", + Label: "Different label", + Status: "ok", + Country: "us", + Capabilities: []string{"Linodes", "NodeBalancers", "Block Storage", "Object Storage"}, + Resolvers: linodego.RegionResolvers{ + IPv4: "192.0.2.4,192.0.2.3", + IPv6: "2001:0db8::,2001:0db8::3", + }, + }, + } + + model := &RegionFilterModel{} + + model.parseRegions(regions) + + assert.Len(t, model.Regions, len(regions)) + + for i, expectedRegion := range regions { + assert.Equal(t, types.StringValue(expectedRegion.ID), model.Regions[i].ID) + assert.Equal(t, types.StringValue(expectedRegion.Label), model.Regions[i].Label) + assert.Equal(t, types.StringValue(expectedRegion.Status), model.Regions[i].Status) + assert.Equal(t, types.StringValue(expectedRegion.Country), model.Regions[i].Country) + for j, capability := range regions[i].Capabilities { + assert.Equal(t, types.StringValue(capability), model.Regions[i].Capabilities[j]) + } + assert.Equal(t, types.StringValue(expectedRegion.Resolvers.IPv4), model.Regions[i].Resolvers[0].IPv4) + assert.Equal(t, types.StringValue(expectedRegion.Resolvers.IPv6), model.Regions[i].Resolvers[0].IPv6) + } +} diff --git a/linode/sshkey/framework_resource_models_unit_test.go b/linode/sshkey/framework_resource_models_unit_test.go new file mode 100644 index 000000000..ca1434704 --- /dev/null +++ b/linode/sshkey/framework_resource_models_unit_test.go @@ -0,0 +1,39 @@ +//go:build unit + +package sshkey + +import ( + "testing" + "time" + + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/linode/linodego" + "github.com/stretchr/testify/assert" +) + +func TestParseConfiguredAttributes(t *testing.T) { + key := linodego.SSHKey{ + Label: "Test Key", + SSHKey: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...", + } + + rm := &ResourceModel{} + rm.parseConfiguredAttributes(&key) + + assert.Equal(t, types.StringValue(key.Label), rm.Label) + assert.Equal(t, types.StringValue(key.SSHKey), rm.SSHKey) +} + +func TestParseComputedAttributes(t *testing.T) { + created := time.Now() + key := linodego.SSHKey{ + ID: 123, + Created: &created, + } + + rm := &ResourceModel{} + rm.parseComputedAttributes(&key) + + assert.Equal(t, types.Int64Value(int64(key.ID)), rm.ID) + assert.Equal(t, types.StringValue(created.Format(time.RFC3339)), rm.Created.StringValue) +} diff --git a/linode/sshkeys/datasource_test.go b/linode/sshkeys/datasource_test.go index 511e5aca0..9c44de583 100644 --- a/linode/sshkeys/datasource_test.go +++ b/linode/sshkeys/datasource_test.go @@ -1,3 +1,5 @@ +//go:build integration + package sshkeys_test import ( diff --git a/linode/sshkeys/framework_models_unit_test.go b/linode/sshkeys/framework_models_unit_test.go new file mode 100644 index 000000000..e0cefaff0 --- /dev/null +++ b/linode/sshkeys/framework_models_unit_test.go @@ -0,0 +1,47 @@ +//go:build unit + +package sshkeys + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/linode/linodego" + "github.com/linode/terraform-provider-linode/linode/sshkey" + "github.com/stretchr/testify/assert" +) + +func TestParseSSHKeys(t *testing.T) { + sshKeys := []linodego.SSHKey{ + { + ID: 1, + Label: "Test Key", + SSHKey: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...", + }, + { + ID: 2, + Label: "Test Key 2", + SSHKey: "ssh-rsa DIFFERENTKEY_EAAAADAQABAAABAQC...", + }, + } + + filterModel := &SSHKeyFilterModel{} + filterModel.parseSSHKeys(context.Background(), sshKeys) + + expectedSSHKey := []sshkey.DataSourceModel{ + { + ID: types.StringValue("1"), + Label: types.StringValue("Test Key"), + SSHKey: types.StringValue("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC..."), + }, + { + ID: types.StringValue("2"), + Label: types.StringValue("Test Key 2"), + SSHKey: types.StringValue("ssh-rsa DIFFERENTKEY_EAAAADAQABAAABAQC..."), + }, + } + + assert.Equal(t, filterModel.SSHKeys[0].SSHKey, expectedSSHKey[0].SSHKey) + assert.Equal(t, filterModel.SSHKeys[1].SSHKey, expectedSSHKey[1].SSHKey) +} diff --git a/linode/stackscript/framework_models_unit_test.go b/linode/stackscript/framework_models_unit_test.go new file mode 100644 index 000000000..171cbe814 --- /dev/null +++ b/linode/stackscript/framework_models_unit_test.go @@ -0,0 +1,110 @@ +//go:build unit + +package stackscript + +import ( + "context" + "testing" + "time" + + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/linode/linodego" + "github.com/stretchr/testify/assert" +) + +func TestParseNonComputedAttributes(t *testing.T) { + createdTime := time.Date(2023, time.August, 17, 12, 0, 0, 0, time.UTC) + updatedTime := time.Date(2023, time.August, 17, 14, 0, 0, 0, time.UTC) + + stackscriptData := &linodego.Stackscript{ + ID: 10079, + Username: "myuser", + Label: "a-stackscript", + Description: "This StackScript installs and configures MySQL\n", + RevNote: "Set up MySQL", + IsPublic: true, + Images: []string{"linode/debian9", "linode/debian8"}, + DeploymentsActive: 1, + Mine: true, + Created: &createdTime, + Updated: &updatedTime, + Script: "\"#!/bin/bash\"\n", + UserDefinedFields: &[]linodego.StackscriptUDF{ + { + Default: "", + Example: "hunter2", + Label: "Enter the password", + ManyOf: "avalue,anothervalue,thirdvalue", + Name: "DB_PASSWORD", + OneOf: "avalue,anothervalue,thirdvalue", + }, + }, + UserGravatarID: "a445b305abda30ebc766bc7fda037c37", + } + + model := &StackScriptModel{} + diagnostics := model.ParseNonComputedAttributes(context.Background(), stackscriptData) + + assert.False(t, diagnostics.HasError(), "No errors should occur during parsing") + + assert.Equal(t, types.StringValue("a-stackscript"), model.Label) + assert.Equal(t, types.StringValue("\"#!/bin/bash\"\n"), model.Script) + assert.Equal(t, types.StringValue("This StackScript installs and configures MySQL\n"), model.Description) + assert.Equal(t, types.StringValue("Set up MySQL"), model.RevNote) + assert.Equal(t, types.BoolValue(true), model.IsPublic) + + for _, image := range stackscriptData.Images { + assert.Contains(t, model.Images.String(), image) + } +} + +func TestParseComputedAttributes(t *testing.T) { + createdTime := time.Date(2023, time.August, 17, 12, 0, 0, 0, time.UTC) + updatedTime := time.Date(2023, time.August, 17, 14, 0, 0, 0, time.UTC) + + stackscriptData := &linodego.Stackscript{ + ID: 10079, + Username: "myuser", + Label: "a-stackscript", + Description: "This StackScript installs and configures MySQL\n", + RevNote: "Set up MySQL", + DeploymentsTotal: 12, + IsPublic: true, + Images: []string{"linode/debian9", "linode/debian8"}, + DeploymentsActive: 1, + Mine: true, + Created: &createdTime, + Updated: &updatedTime, + Script: "\"#!/bin/bash\"\n", + UserDefinedFields: &[]linodego.StackscriptUDF{ + { + Default: "", + Example: "hunter2", + Label: "Enter the password", + ManyOf: "avalue,anothervalue,thirdvalue", + Name: "DB_PASSWORD", + OneOf: "avalue,anothervalue,thirdvalue", + }, + }, + UserGravatarID: "a445b305abda30ebc766bc7fda037c37", + } + + model := &StackScriptModel{} + diagnostics := model.ParseComputedAttributes(context.Background(), stackscriptData) + + assert.False(t, diagnostics.HasError(), "No errors should occur during parsing") + + assert.Equal(t, types.StringValue("10079"), model.ID) + assert.Equal(t, types.Int64Value(1), model.DeploymentsActive) + assert.Equal(t, types.StringValue("a445b305abda30ebc766bc7fda037c37"), model.UserGravatarID) + assert.Equal(t, types.Int64Value(12), model.DeploymentsTotal) + assert.Equal(t, types.StringValue("myuser"), model.Username) + + assert.NotNil(t, model.Created) + assert.NotNil(t, model.Updated) + + udfs := model.UserDefinedFields + assert.Contains(t, udfs.String(), "Enter the password") + assert.Contains(t, udfs.String(), "DB_PASSWORD") + assert.Contains(t, udfs.String(), "avalue,anothervalue,thirdvalue") +} diff --git a/linode/stackscripts/framework_models_unit_test.go b/linode/stackscripts/framework_models_unit_test.go new file mode 100644 index 000000000..15fe37701 --- /dev/null +++ b/linode/stackscripts/framework_models_unit_test.go @@ -0,0 +1,71 @@ +//go:build unit + +package stackscripts + +import ( + "context" + "strconv" + "testing" + "time" + + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/linode/linodego" + "github.com/stretchr/testify/assert" +) + +func TestParseStackscripts(t *testing.T) { + createdTime := time.Date(2023, time.August, 17, 12, 0, 0, 0, time.UTC) + updatedTime := time.Date(2023, time.August, 17, 14, 0, 0, 0, time.UTC) + + stackscriptsData := []linodego.Stackscript{ + { + ID: 10079, + Username: "myuser", + Label: "a-stackscript", + Description: "This StackScript installs and configures MySQL\n", + RevNote: "Set up MySQL", + IsPublic: true, + Images: []string{"linode/debian9", "linode/debian8"}, + DeploymentsActive: 1, + Mine: true, + Created: &createdTime, + Updated: &updatedTime, + Script: "\"#!/bin/bash\"\n", + UserDefinedFields: &[]linodego.StackscriptUDF{ + { + Default: "", + Example: "hunter2", + Label: "Enter the password", + ManyOf: "avalue,anothervalue,thirdvalue", + Name: "DB_PASSWORD", + OneOf: "avalue,anothervalue,thirdvalue", + }, + }, + UserGravatarID: "a445b305abda30ebc766bc7fda037c37", + }, + } + + model := &StackscriptFilterModel{} + diagnostics := model.parseStackscripts(context.Background(), stackscriptsData) + + assert.False(t, diagnostics.HasError(), "No errors should occur during parsing") + + for i, stackscript := range stackscriptsData { + // Non computed attrs assertions + assert.Contains(t, model.Stackscripts[i].ID.String(), strconv.Itoa(stackscript.ID)) + assert.Equal(t, types.StringValue(stackscript.Description), model.Stackscripts[i].Description) + assert.Equal(t, types.StringValue(stackscript.Script), model.Stackscripts[i].Script) + assert.Equal(t, types.StringValue(stackscript.RevNote), model.Stackscripts[i].RevNote) + assert.Equal(t, types.BoolValue(stackscript.IsPublic), model.Stackscripts[i].IsPublic) + + for _, image := range stackscript.Images { + assert.Contains(t, model.Stackscripts[i].Images.String(), image) + } + + // Computed attr assertions + assert.Equal(t, types.Int64Value(int64(stackscript.DeploymentsActive)), model.Stackscripts[i].DeploymentsActive) + assert.Equal(t, types.Int64Value(int64(stackscript.DeploymentsTotal)), model.Stackscripts[i].DeploymentsTotal) + assert.Equal(t, types.StringValue(stackscript.UserGravatarID), model.Stackscripts[i].UserGravatarID) + assert.Equal(t, types.StringValue(stackscript.Username), model.Stackscripts[i].Username) + } +} diff --git a/linode/token/framework_models_unit_test.go b/linode/token/framework_models_unit_test.go new file mode 100644 index 000000000..341af8430 --- /dev/null +++ b/linode/token/framework_models_unit_test.go @@ -0,0 +1,57 @@ +//go:build unit + +package token + +import ( + "strconv" + "testing" + "time" + + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/linode/linodego" + "github.com/linode/terraform-provider-linode/linode/helper/customtypes" + "github.com/stretchr/testify/assert" +) + +func TestParseToken(t *testing.T) { + createdTime := time.Date(2023, time.August, 17, 12, 0, 0, 0, time.UTC) + expiryDate := time.Date(2050, time.August, 17, 12, 0, 0, 0, time.UTC) + + sampleToken := linodego.Token{ + ID: 123, + Scopes: "*", + Label: "Test Token", + Token: "test-token-value", + Created: &createdTime, + Expiry: &expiryDate, + } + + model := &ResourceModel{} + + model.parseToken(&sampleToken, false) + + assert.Equal(t, types.StringValue(sampleToken.Label), model.Label) + assert.Equal(t, customtypes.LinodeScopesStringValue{StringValue: types.StringValue(sampleToken.Scopes)}, model.Scopes) + assert.Equal(t, types.StringValue(sampleToken.Token), model.Token) + assert.Equal(t, types.StringValue(strconv.Itoa(sampleToken.ID)), model.ID) +} + +func TestParseTokenRefreshTrue(t *testing.T) { + createdTime := time.Date(2023, time.August, 17, 12, 0, 0, 0, time.UTC) + expiryDate := time.Date(2050, time.August, 17, 12, 0, 0, 0, time.UTC) + + sampleToken := linodego.Token{ + ID: 456, + Scopes: "linodes", + Label: "Another Token", + Token: "another-token-value", + Created: &createdTime, + Expiry: &expiryDate, // Set to nil for testing purposes + } + + rm := &ResourceModel{} + + rm.parseToken(&sampleToken, true) + + assert.Empty(t, rm.Token) +} diff --git a/linode/user/framework_models_unit_test.go b/linode/user/framework_models_unit_test.go new file mode 100644 index 000000000..45f67bb1a --- /dev/null +++ b/linode/user/framework_models_unit_test.go @@ -0,0 +1,154 @@ +//go:build unit + +package user + +import ( + "context" + "github.com/hashicorp/terraform-plugin-framework/types" + "strconv" + "testing" + "time" + + "github.com/linode/linodego" + "github.com/stretchr/testify/assert" +) + +func TestParseUser(t *testing.T) { + // Sample data + phoneNumber := "+5555555555" + userData := linodego.User{ + Username: "example_user", + Email: "example_user@linode.com", + Restricted: true, + TFAEnabled: true, + SSHKeys: []string{"home-pc", "laptop"}, + PasswordCreated: &time.Time{}, + VerifiedPhoneNumber: &phoneNumber, + } + + dataModel := &DataSourceModel{} + + diags := dataModel.ParseUser(context.Background(), &userData) + + assert.False(t, diags.HasError()) + + assert.Equal(t, types.StringValue("example_user"), dataModel.Username) + assert.Equal(t, types.StringValue("example_user@linode.com"), dataModel.Email) + assert.Equal(t, types.BoolValue(true), dataModel.Restricted) + assert.Equal(t, types.BoolValue(true), dataModel.TFAEnabled) + for _, key := range userData.SSHKeys { + assert.Contains(t, dataModel.SSHKeys.String(), key) + } + assert.Contains(t, dataModel.VerifiedPhoneNumber.String(), "+5555555555") +} + +func TestParseUserGrants(t *testing.T) { + permissionLevel := linodego.AccessLevelReadOnly + userGrantsData := linodego.UserGrants{ + Database: []linodego.GrantedEntity{ + { + ID: 123, + Label: "example-database", + Permissions: "read_only", + }, + }, + Domain: []linodego.GrantedEntity{ + { + ID: 456, + Label: "example-domain", + Permissions: "read_write", + }, + }, + Firewall: []linodego.GrantedEntity{ + { + ID: 789, + Label: "example-firewall", + Permissions: "read_only", + }, + }, + Global: linodego.GlobalUserGrants{ + AccountAccess: &permissionLevel, + AddDatabases: true, + AddDomains: true, + AddFirewalls: true, + AddImages: true, + AddLinodes: true, + AddLongview: true, + AddNodeBalancers: true, + AddStackScripts: true, + AddVolumes: true, + CancelAccount: false, + LongviewSubscription: true, + }, + Image: []linodego.GrantedEntity{ + { + ID: 101, + Label: "example-image", + Permissions: "read_write", + }, + }, + Linode: []linodego.GrantedEntity{ + { + ID: 102, + Label: "example-linode", + Permissions: "read_write", + }, + }, + Longview: []linodego.GrantedEntity{ + { + ID: 103, + Label: "example-longview", + Permissions: "read_only", + }, + }, + NodeBalancer: []linodego.GrantedEntity{ + { + ID: 104, + Label: "example-nodebalancer", + Permissions: "read_only", + }, + }, + StackScript: []linodego.GrantedEntity{ + { + ID: 105, + Label: "example-stackscript", + Permissions: "read_write", + }, + }, + Volume: []linodego.GrantedEntity{ + { + ID: 106, + Label: "example-volume", + Permissions: "read_only", + }, + }, + } + + dataModel := &DataSourceModel{} + + diags := dataModel.ParseUserGrants(context.Background(), &userGrantsData) + + assert.False(t, diags.HasError()) + assert.Contains(t, dataModel.DatabaseGrant.String(), strconv.Itoa(userGrantsData.Database[0].ID)) + assert.Contains(t, dataModel.FirewallGrant.String(), strconv.Itoa(userGrantsData.Firewall[0].ID)) + assert.Contains(t, dataModel.ImageGrant.String(), strconv.Itoa(userGrantsData.Image[0].ID)) + assert.Contains(t, dataModel.LinodeGrant.String(), strconv.Itoa(userGrantsData.Linode[0].ID)) + assert.Contains(t, dataModel.LongviewGrant.String(), strconv.Itoa(userGrantsData.Longview[0].ID)) + assert.Contains(t, dataModel.NodebalancerGrant.String(), strconv.Itoa(userGrantsData.NodeBalancer[0].ID)) + assert.Contains(t, dataModel.StackscriptGrant.String(), strconv.Itoa(userGrantsData.StackScript[0].ID)) + assert.Contains(t, dataModel.VolumeGrant.String(), strconv.Itoa(userGrantsData.Volume[0].ID)) + + assert.Contains(t, dataModel.GlobalGrants.String(), "\"account_access\":\"read_only\"") + assert.Contains(t, dataModel.GlobalGrants.String(), "\"add_databases\":true") + assert.Contains(t, dataModel.GlobalGrants.String(), "\"add_domains\":true") + assert.Contains(t, dataModel.GlobalGrants.String(), "\"add_firewalls\":true") + assert.Contains(t, dataModel.GlobalGrants.String(), "\"add_images\":true") + assert.Contains(t, dataModel.GlobalGrants.String(), "\"add_linodes\":true") + assert.Contains(t, dataModel.GlobalGrants.String(), "\"add_longview\":true") + assert.Contains(t, dataModel.GlobalGrants.String(), "\"add_nodebalancers\":true") + assert.Contains(t, dataModel.GlobalGrants.String(), "\"add_stackscripts\":true") + assert.Contains(t, dataModel.GlobalGrants.String(), "\"add_volumes\":true") + assert.Contains(t, dataModel.GlobalGrants.String(), "\"cancel_account\":false") + assert.Contains(t, dataModel.GlobalGrants.String(), "\"longview_subscription\":true") + +} diff --git a/linode/vlan/framework_models_unit_test.go b/linode/vlan/framework_models_unit_test.go new file mode 100644 index 000000000..1912162cf --- /dev/null +++ b/linode/vlan/framework_models_unit_test.go @@ -0,0 +1,57 @@ +//go:build unit + +package vlan + +import ( + "context" + "strconv" + "testing" + "time" + + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/linode/linodego" + "github.com/stretchr/testify/assert" +) + +func TestParseVLANs(t *testing.T) { + vlans := []linodego.VLAN{ + { + Label: "VLAN 1", + Linodes: []int{123, 456}, + Region: "us-east", + Created: &time.Time{}, + }, + { + Label: "VLAN 2", + Linodes: []int{789}, + Region: "us-west", + Created: nil, + }, + } + + data := &VLANsFilterModel{} + diags := data.parseVLANs(context.Background(), vlans) + + assert.Empty(t, diags.HasError(), "No errors expected") + assert.Equal(t, len(vlans), len(data.VLANs), "Number of parsed VLANs should match") +} + +func TestParseVLAN(t *testing.T) { + vlan := linodego.VLAN{ + Label: "Test VLAN", + Linodes: []int{123, 456}, + Region: "us-east", + Created: &time.Time{}, + } + + data := &VLANModel{} + diags := data.parseVLAN(context.Background(), vlan) + + assert.Empty(t, diags.HasError(), "No errors expected") + assert.Equal(t, types.StringValue(vlan.Label), data.Label) + for _, linodeId := range vlan.Linodes { + assert.Contains(t, data.Linodes.String(), strconv.Itoa(linodeId)) + } + assert.Equal(t, types.StringValue(vlan.Region), data.Region) + assert.NotNil(t, data.Created) +} diff --git a/linode/volume/framework_models_unit_test.go b/linode/volume/framework_models_unit_test.go new file mode 100644 index 000000000..fdf4458b7 --- /dev/null +++ b/linode/volume/framework_models_unit_test.go @@ -0,0 +1,58 @@ +//go:build unit + +package volume + +import ( + "context" + "testing" + "time" + + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/linode/linodego" + "github.com/stretchr/testify/assert" +) + +func TestVolumeModelParsing(t *testing.T) { + createdTime := time.Date(2023, time.August, 17, 12, 0, 0, 0, time.UTC) + updatedTime := time.Date(2023, time.August, 18, 12, 0, 0, 0, time.UTC) + linodeId := 12346 + + data := &VolumeModel{} + volumeData := &linodego.Volume{ + ID: 12345, + Label: "my-volume", + Status: "active", + Region: "us-east", + Size: 30, + LinodeID: &linodeId, + FilesystemPath: "/dev/disk/by-id/scsi-0Linode_Volume_my-volume", + Tags: []string{"example tag", "another example"}, + Created: &createdTime, + Updated: &updatedTime, + } + + ctx := context.Background() + + // Test parseComputedAttributes + diags := data.parseComputedAttributes(ctx, volumeData) + assert.Empty(t, diags) + + assert.Equal(t, types.Int64Value(12345), data.ID) + assert.Equal(t, types.StringValue("active"), data.Status) + assert.Equal(t, types.StringValue("us-east"), data.Region) + assert.Equal(t, types.Int64Value(30), data.Size) + assert.Equal(t, types.Int64Value(12346), data.LinodeID) + assert.Equal(t, types.StringValue("/dev/disk/by-id/scsi-0Linode_Volume_my-volume"), data.FilesystemPath) + + assert.NotContains(t, data.Tags.String(), "example tag") + assert.NotContains(t, data.Tags.String(), "another example") + assert.NotNil(t, data.Created) + assert.NotNil(t, data.Updated) + + // Test parseNonComputedAttributes + diags = data.parseNonComputedAttributes(ctx, volumeData) + assert.Empty(t, diags) + assert.Contains(t, data.Tags.String(), "example tag") + assert.Contains(t, data.Tags.String(), "another example") + assert.Equal(t, types.StringValue("my-volume"), data.Label) +} diff --git a/makefile b/makefile index a15d305c4..44f195ffd 100644 --- a/makefile +++ b/makefile @@ -58,6 +58,7 @@ smoketest: fmtcheck unittest: go test -v --tags=unit ./linode/... + vet: @echo "go vet ." @go vet $$(go list ./...) ; if [ $$? -eq 1 ]; then \