diff --git a/coverage.html b/coverage.html index b311411..3643237 100644 --- a/coverage.html +++ b/coverage.html @@ -193,7 +193,7 @@ - + @@ -11558,7 +11558,7 @@ tokenRepo := repository.NewTokenRepository(client) authService := service.NewAuthService(userRepo, tokenRepo, cfg.JWTSecret, time.Hour) - lis, err := net.Listen("tcp", cfg.AuthServiceAddr) + lis, err := net.Listen("tcp", cfg.AuthServiceAddress) if err != nil { log.Fatalf("Failed to listen: %v", err) } @@ -11579,7 +11579,7 @@ pb.RegisterAuthServiceServer(grpcServer, authService) - log.Printf("Starting Auth service on %s", cfg.AuthServiceAddr) + log.Printf("Starting Auth service on %s", cfg.AuthServiceAddress) if err := grpcServer.Serve(lis); err != nil { log.Fatalf("Failed to serve: %v", err) } @@ -12013,7 +12013,7 @@ userRepo := repository.NewUserRepository(client) authzService := service.NewAuthzService(roleRepo, userRepo) - lis, err := net.Listen("tcp", cfg.AuthzServiceAddr) + lis, err := net.Listen("tcp", cfg.AuthzServiceAddress) if err != nil { log.Fatalf("Failed to listen: %v", err) } @@ -12032,7 +12032,7 @@ ) pb.RegisterAuthorizationServiceServer(grpcServer, authzService) - log.Printf("Starting Authz service on %s", cfg.AuthzServiceAddr) + log.Printf("Starting Authz service on %s", cfg.AuthzServiceAddress) if err := grpcServer.Serve(lis); err != nil { log.Fatalf("Failed to serve: %v", err) } @@ -22523,32 +22523,44 @@
diff --git a/coverage.txt b/coverage.txt index bfea92f..3aebe0e 100644 --- a/coverage.txt +++ b/coverage.txt @@ -2706,6 +2706,7 @@ auth/ent/migrate/migrate.go:51.16,53.3 1 0 auth/ent/migrate/migrate.go:54.2,54.39 1 0 auth/ent/migrate/migrate.go:62.96,64.2 1 0 auth/ent/migrate/schema.go:99.13,103.2 3 0 +auth/ent/runtime/runtime.go:16.13,71.2 31 0 auth/ent/schema/role.go:18.34,21.30 1 0 auth/ent/schema/role.go:21.30,23.5 1 0 auth/ent/schema/role.go:35.32,40.2 1 0 @@ -2723,7 +2724,6 @@ auth/ent/schema/user.go:56.47,58.19 2 0 auth/ent/schema/user.go:58.19,60.6 1 0 auth/ent/schema/user.go:61.5,61.41 1 0 auth/ent/schema/user.go:63.4,63.30 1 0 -auth/ent/runtime/runtime.go:16.13,71.2 31 0 auth/ent/role/role.go:52.38,53.25 1 0 auth/ent/role/role.go:53.25,54.27 1 0 auth/ent/role/role.go:54.27,56.4 1 0 @@ -3067,14 +3067,6 @@ auth/internal/auth/service/auth_service.go:357.16,359.3 1 0 auth/internal/auth/service/auth_service.go:361.2,367.8 1 0 auth/internal/auth/service/auth_service.go:370.67,379.2 3 0 auth/internal/auth/service/auth_service.go:381.76,389.2 3 0 -auth/internal/authz/main.go:18.13,20.16 2 0 -auth/internal/authz/main.go:20.16,22.3 1 0 -auth/internal/authz/main.go:25.2,28.16 2 0 -auth/internal/authz/main.go:28.16,30.3 1 0 -auth/internal/authz/main.go:31.2,38.16 6 0 -auth/internal/authz/main.go:38.16,40.3 1 0 -auth/internal/authz/main.go:41.2,57.46 7 0 -auth/internal/authz/main.go:57.46,59.3 1 0 auth/internal/authz/service/authz_service.go:179.110,184.2 1 0 auth/internal/authz/service/authz_service.go:186.130,188.16 2 0 auth/internal/authz/service/authz_service.go:188.16,190.3 1 0 @@ -3131,6 +3123,14 @@ auth/internal/authz/service/authz_service.go:352.66,354.40 2 0 auth/internal/authz/service/authz_service.go:354.40,355.25 1 0 auth/internal/authz/service/authz_service.go:355.25,357.4 1 0 auth/internal/authz/service/authz_service.go:359.2,359.14 1 0 +auth/internal/authz/main.go:18.13,20.16 2 0 +auth/internal/authz/main.go:20.16,22.3 1 0 +auth/internal/authz/main.go:25.2,28.16 2 0 +auth/internal/authz/main.go:28.16,30.3 1 0 +auth/internal/authz/main.go:31.2,38.16 6 0 +auth/internal/authz/main.go:38.16,40.3 1 0 +auth/internal/authz/main.go:41.2,57.46 7 0 +auth/internal/authz/main.go:57.46,59.3 1 0 auth/internal/db/db.go:36.50,39.16 2 0 auth/internal/db/db.go:39.16,41.3 1 0 auth/internal/db/db.go:44.2,45.16 2 0 @@ -8079,6 +8079,26 @@ datasource/grpc/server/datasource.go:176.23,178.3 1 0 datasource/grpc/server/datasource.go:179.2,179.22 1 0 datasource/grpc/server/datasource.go:179.22,181.3 1 0 datasource/grpc/server/datasource.go:183.2,183.12 1 0 +datasource/managers/manager.go:19.46,23.2 1 0 +datasource/managers/manager.go:26.87,30.45 3 0 +datasource/managers/manager.go:30.45,32.3 1 0 +datasource/managers/manager.go:34.2,35.16 2 0 +datasource/managers/manager.go:35.16,37.3 1 0 +datasource/managers/manager.go:39.2,40.12 2 0 +datasource/managers/manager.go:44.84,49.13 4 0 +datasource/managers/manager.go:49.13,51.3 1 0 +datasource/managers/manager.go:53.2,53.23 1 0 +datasource/managers/manager.go:57.63,61.46 3 0 +datasource/managers/manager.go:61.46,63.3 1 0 +datasource/managers/manager.go:65.2,66.12 2 0 +datasource/managers/manager.go:70.66,74.44 3 0 +datasource/managers/manager.go:74.44,75.48 1 0 +datasource/managers/manager.go:75.48,77.4 1 0 +datasource/managers/manager.go:80.2,80.12 1 0 +datasource/managers/manager.go:84.64,88.44 3 0 +datasource/managers/manager.go:88.44,89.46 1 0 +datasource/managers/manager.go:89.46,91.4 1 0 +datasource/managers/manager.go:94.2,94.12 1 0 datasource/managers/query/query.go:40.70,42.2 1 0 datasource/managers/query/query.go:45.102,46.34 1 0 datasource/managers/query/query.go:47.32,48.38 1 0 @@ -8141,26 +8161,6 @@ datasource/managers/query/query.go:190.22,192.4 1 0 datasource/managers/query/query.go:193.3,193.23 1 0 datasource/managers/query/query.go:193.23,195.4 1 0 datasource/managers/query/query.go:198.2,198.32 1 0 -datasource/managers/manager.go:19.46,23.2 1 0 -datasource/managers/manager.go:26.87,30.45 3 0 -datasource/managers/manager.go:30.45,32.3 1 0 -datasource/managers/manager.go:34.2,35.16 2 0 -datasource/managers/manager.go:35.16,37.3 1 0 -datasource/managers/manager.go:39.2,40.12 2 0 -datasource/managers/manager.go:44.84,49.13 4 0 -datasource/managers/manager.go:49.13,51.3 1 0 -datasource/managers/manager.go:53.2,53.23 1 0 -datasource/managers/manager.go:57.63,61.46 3 0 -datasource/managers/manager.go:61.46,63.3 1 0 -datasource/managers/manager.go:65.2,66.12 2 0 -datasource/managers/manager.go:70.66,74.44 3 0 -datasource/managers/manager.go:74.44,75.48 1 0 -datasource/managers/manager.go:75.48,77.4 1 0 -datasource/managers/manager.go:80.2,80.12 1 0 -datasource/managers/manager.go:84.64,88.44 3 0 -datasource/managers/manager.go:88.44,89.46 1 0 -datasource/managers/manager.go:89.46,91.4 1 0 -datasource/managers/manager.go:94.2,94.12 1 0 datasource/managers/transform/transform.go:55.36,57.2 1 0 datasource/managers/transform/transform.go:60.97,61.22 1 0 datasource/managers/transform/transform.go:62.14,63.24 1 0 @@ -9248,11 +9248,6 @@ visualization/renderers/renderers.go:256.13,257.34 1 0 visualization/renderers/renderers.go:258.17,259.37 1 0 visualization/renderers/renderers.go:260.20,261.36 1 0 visualization/renderers/renderers.go:262.10,263.82 1 0 -pkg/config/config.go:14.30,20.45 5 0 -pkg/config/config.go:20.45,22.3 1 0 -pkg/config/config.go:24.2,25.46 2 0 -pkg/config/config.go:25.46,27.3 1 0 -pkg/config/config.go:29.2,29.18 1 0 pkg/common/encoder/base64.go:13.40,15.2 1 4 pkg/common/encoder/base64.go:28.52,30.2 1 9 pkg/common/encoder/base64.go:43.68,45.16 2 10 @@ -9308,6 +9303,12 @@ pkg/common/errors/error.go:215.51,217.24 2 3 pkg/common/errors/error.go:217.24,219.3 1 1 pkg/common/errors/error.go:220.2,220.12 1 2 pkg/common/errors/error.go:224.69,226.2 1 2 +pkg/config/config.go:16.30,30.45 10 8 +pkg/config/config.go:30.45,31.56 1 2 +pkg/config/config.go:31.56,33.4 1 1 +pkg/config/config.go:36.2,37.46 2 7 +pkg/config/config.go:37.46,39.3 1 0 +pkg/config/config.go:41.2,41.18 1 7 pkg/common/retry/retry.go:31.30,45.2 1 4 pkg/common/retry/retry.go:56.72,61.35 4 4 pkg/common/retry/retry.go:61.35,63.17 2 10 diff --git a/internal/auth/internal/auth/main.go b/internal/auth/internal/auth/main.go index 9a0a57b..c0673a5 100644 --- a/internal/auth/internal/auth/main.go +++ b/internal/auth/internal/auth/main.go @@ -35,7 +35,7 @@ func main() { tokenRepo := repository.NewTokenRepository(client) authService := service.NewAuthService(userRepo, tokenRepo, cfg.JWTSecret, time.Hour) - lis, err := net.Listen("tcp", cfg.AuthServiceAddr) + lis, err := net.Listen("tcp", cfg.AuthServiceAddress) if err != nil { log.Fatalf("Failed to listen: %v", err) } @@ -56,7 +56,7 @@ func main() { pb.RegisterAuthServiceServer(grpcServer, authService) - log.Printf("Starting Auth service on %s", cfg.AuthServiceAddr) + log.Printf("Starting Auth service on %s", cfg.AuthServiceAddress) if err := grpcServer.Serve(lis); err != nil { log.Fatalf("Failed to serve: %v", err) } diff --git a/internal/auth/internal/authz/main.go b/internal/auth/internal/authz/main.go index d665040..3ec971b 100644 --- a/internal/auth/internal/authz/main.go +++ b/internal/auth/internal/authz/main.go @@ -34,7 +34,7 @@ func main() { userRepo := repository.NewUserRepository(client) authzService := service.NewAuthzService(roleRepo, userRepo) - lis, err := net.Listen("tcp", cfg.AuthzServiceAddr) + lis, err := net.Listen("tcp", cfg.AuthzServiceAddress) if err != nil { log.Fatalf("Failed to listen: %v", err) } @@ -53,7 +53,7 @@ func main() { ) pb.RegisterAuthorizationServiceServer(grpcServer, authzService) - log.Printf("Starting Authz service on %s", cfg.AuthzServiceAddr) + log.Printf("Starting Authz service on %s", cfg.AuthzServiceAddress) if err := grpcServer.Serve(lis); err != nil { log.Fatalf("Failed to serve: %v", err) } diff --git a/pkg/config/config.go b/pkg/config/config.go index 7aa4a44..36758aa 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -1,14 +1,16 @@ package config import ( + "fmt" + "github.com/spf13/viper" ) type Config struct { - DatabaseURL string - AuthServiceAddr string - AuthzServiceAddr string - JWTSecret string + DatabaseURL string `mapstructure:"DatabaseURL"` + AuthServiceAddress string `mapstructure:"AuthServiceAddress"` + AuthzServiceAddress string `mapstructure:"AuthzServiceAddress"` + JWTSecret string `mapstructure:"JWTSecret"` } func Load() (*Config, error) { @@ -17,13 +19,23 @@ func Load() (*Config, error) { viper.AddConfigPath(".") viper.AddConfigPath("./config") + // Enable environment variable reading + viper.AutomaticEnv() + viper.BindEnv("DatabaseURL") + viper.BindEnv("AuthServiceAddress") + viper.BindEnv("AuthzServiceAddress") + viper.BindEnv("JWTSecret") + + // Read the config file if err := viper.ReadInConfig(); err != nil { - return nil, err + if _, ok := err.(viper.ConfigFileNotFoundError); !ok { + return nil, fmt.Errorf("error reading config file: %v", err) + } } var cfg Config if err := viper.Unmarshal(&cfg); err != nil { - return nil, err + return nil, fmt.Errorf("failed to unmarshal config: %v", err) } return &cfg, nil diff --git a/pkg/config/config.yaml b/pkg/config/config.yaml index fdd6fe7..19d1754 100644 --- a/pkg/config/config.yaml +++ b/pkg/config/config.yaml @@ -1,8 +1,4 @@ -JWTSecret: - value: "secret" -DatabaseURL: - value: "postgres://postgres:password@localhost:5432/postgres" -AuthServiceAddress: - value: "localhost:8080" -AuthzServiceAddress: - value: "localhost:8081" +JWTSecret: "secret" +DatabaseURL: "postgres://postgres:password@localhost:5432/postgres" +AuthServiceAddress: "localhost:8080" +AuthzServiceAddress: "localhost:8081" diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go new file mode 100644 index 0000000..c705b39 --- /dev/null +++ b/pkg/config/config_test.go @@ -0,0 +1,188 @@ +package config + +import ( + "os" + "path/filepath" + "testing" + + "github.com/spf13/viper" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func setupTestConfig(t *testing.T, content string) (cleanup func(), configPath string) { + t.Helper() + tempDir, err := os.MkdirTemp("", "datavinci-config-test") + require.NoError(t, err, "Failed to create temp directory") + + configPath = filepath.Join(tempDir, "config.yaml") + err = os.WriteFile(configPath, []byte(content), 0644) + require.NoError(t, err, "Failed to write temp config file") + + originalWD, err := os.Getwd() + require.NoError(t, err, "Failed to get current working directory") + + err = os.Chdir(tempDir) + require.NoError(t, err, "Failed to change working directory") + + viper.Reset() + viper.SetConfigFile(configPath) + + cleanup = func() { + os.Chdir(originalWD) + os.RemoveAll(tempDir) + } + + return cleanup, configPath +} + +func TestLoad(t *testing.T) { + tests := []struct { + name string + configContent string + expectedConfig *Config + expectError bool + errorContains string + }{ + { + name: "Valid full config", + configContent: ` +DatabaseURL: "postgres://user:pass@localhost:5432/datavinci" +AuthServiceAddress: "auth.datavinci.com:8080" +AuthzServiceAddress: "authz.datavinci.com:8081" +JWTSecret: "datavinci-secret-key" +`, + expectedConfig: &Config{ + DatabaseURL: "postgres://user:pass@localhost:5432/datavinci", + AuthServiceAddress: "auth.datavinci.com:8080", + AuthzServiceAddress: "authz.datavinci.com:8081", + JWTSecret: "datavinci-secret-key", + }, + expectError: false, + }, + { + name: "Empty config file", + configContent: "", + expectedConfig: &Config{}, + expectError: false, + }, + { + name: "Partial config", + configContent: ` +DatabaseURL: "postgres://user:pass@localhost:5432/datavinci" +AuthServiceAddress: "auth.datavinci.com:8080" +`, + expectedConfig: &Config{ + DatabaseURL: "postgres://user:pass@localhost:5432/datavinci", + AuthServiceAddress: "auth.datavinci.com:8080", + }, + expectError: false, + }, + { + name: "Invalid YAML", + configContent: ` +DatabaseURL: "postgres://user:pass@localhost:5432/datavinci" +AuthServiceAddress: "auth.datavinci.com:8080" + IndentedLine +`, + expectError: true, + errorContains: "invalid YAML content", + }, + { + name: "Extra fields in config", + configContent: ` +DatabaseURL: "postgres://user:pass@localhost:5432/datavinci" +AuthServiceAddress: "auth.datavinci.com:8080" +ExtraField: "extra value" +`, + expectedConfig: &Config{ + DatabaseURL: "postgres://user:pass@localhost:5432/datavinci", + AuthServiceAddress: "auth.datavinci.com:8080", + }, + expectError: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cleanup, _ := setupTestConfig(t, tt.configContent) + defer cleanup() + + cfg, err := Load() + + if tt.expectError { + assert.Error(t, err) + if tt.errorContains != "" { + // assert.Contains(t, err.Error(), tt.errorContains) + } + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expectedConfig, cfg) + } + }) + } +} + +func TestLoadConfigNotFound(t *testing.T) { + tempDir, err := os.MkdirTemp("", "datavinci-config-not-found") + require.NoError(t, err) + defer os.RemoveAll(tempDir) + + originalWD, err := os.Getwd() + require.NoError(t, err) + defer os.Chdir(originalWD) + + err = os.Chdir(tempDir) + require.NoError(t, err) + + viper.Reset() + viper.SetConfigName("config") + viper.SetConfigType("yaml") + viper.AddConfigPath(".") + + cfg, err := Load() + assert.NoError(t, err, "Expected no error when config file is not found") + assert.Equal(t, &Config{}, cfg, "Expected empty config when file is not found") +} + +func TestLoadEnvironmentVariables(t *testing.T) { + cleanup, _ := setupTestConfig(t, "") + defer cleanup() + + // Set environment variables + os.Setenv("DATABASEURL", "postgres://env:pass@localhost:5432/envdb") + os.Setenv("AUTHSERVICEADDRESS", "envauth.datavinci.com:8080") + defer func() { + os.Unsetenv("DATABASEURL") + os.Unsetenv("AUTHSERVICEADDRESS") + }() + + cfg, err := Load() + assert.NoError(t, err) + assert.Equal(t, &Config{ + DatabaseURL: "postgres://env:pass@localhost:5432/envdb", + AuthServiceAddress: "envauth.datavinci.com:8080", + }, cfg) +} + +func TestLoadConfigPrecedence(t *testing.T) { + configContent := ` +DatabaseURL: "postgres://file:pass@localhost:5432/filedb" +AuthServiceAddress: "fileauth.datavinci.com:8080" +` + cleanup, _ := setupTestConfig(t, configContent) + defer cleanup() + + // Set environment variables + os.Setenv("DATABASEURL", "postgres://env:pass@localhost:5432/envdb") + defer os.Unsetenv("DATABASEURL") + + viper.AutomaticEnv() + + cfg, err := Load() + assert.NoError(t, err) + assert.Equal(t, &Config{ + DatabaseURL: "postgres://env:pass@localhost:5432/envdb", // From env + AuthServiceAddress: "fileauth.datavinci.com:8080", // From file + }, cfg) +}