diff options
Diffstat (limited to 'lib/instance_test.go')
| -rw-r--r-- | lib/instance_test.go | 383 |
1 files changed, 383 insertions, 0 deletions
diff --git a/lib/instance_test.go b/lib/instance_test.go new file mode 100644 index 00000000..9ef1b7e9 --- /dev/null +++ b/lib/instance_test.go @@ -0,0 +1,383 @@ +package lib + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "testing" +) + +func TestNewInstance(t *testing.T) { + instance, err := NewInstance() + if err != nil { + t.Errorf("NewInstance() error = %v", err) + } + if instance == nil { + t.Error("NewInstance() returned nil") + } +} + +func TestInstance_AddInput(t *testing.T) { + instance, _ := NewInstance() + mockInput := &mockInputConverter{typ: "test"} + + instance.AddInput(mockInput) + + // We can't directly access the input slice, but we can test Run behavior + // This is tested indirectly through other tests +} + +func TestInstance_AddOutput(t *testing.T) { + instance, _ := NewInstance() + mockOutput := &mockOutputConverter{typ: "test"} + + instance.AddOutput(mockOutput) + + // Similar to AddInput, tested indirectly +} + +func TestInstance_ResetInput(t *testing.T) { + instance, _ := NewInstance() + mockInput := &mockInputConverter{typ: "test"} + + instance.AddInput(mockInput) + instance.ResetInput() + + // After reset, Run should fail due to no inputs + err := instance.Run() + if err == nil { + t.Error("Instance.Run() should fail after ResetInput") + } +} + +func TestInstance_ResetOutput(t *testing.T) { + instance, _ := NewInstance() + mockOutput := &mockOutputConverter{typ: "test"} + + instance.AddOutput(mockOutput) + instance.ResetOutput() + + // After reset, Run should fail due to no outputs + err := instance.Run() + if err == nil { + t.Error("Instance.Run() should fail after ResetOutput") + } +} + +func TestInstance_Run(t *testing.T) { + tests := []struct { + name string + setupFn func(Instance) + wantErr bool + }{ + { + name: "no input or output", + setupFn: func(i Instance) { + // Don't add anything + }, + wantErr: true, + }, + { + name: "only input", + setupFn: func(i Instance) { + i.AddInput(&mockInputConverter{typ: "test"}) + }, + wantErr: true, + }, + { + name: "only output", + setupFn: func(i Instance) { + i.AddOutput(&mockOutputConverter{typ: "test"}) + }, + wantErr: true, + }, + { + name: "both input and output", + setupFn: func(i Instance) { + i.AddInput(&mockInputConverter{typ: "test"}) + i.AddOutput(&mockOutputConverter{typ: "test"}) + }, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + instance, _ := NewInstance() + tt.setupFn(instance) + + err := instance.Run() + if (err != nil) != tt.wantErr { + t.Errorf("Instance.Run() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestInstance_RunInput(t *testing.T) { + instance, _ := NewInstance() + instance.AddInput(&mockInputConverter{typ: "test"}) + + container := NewContainer() + err := instance.RunInput(container) + if err != nil { + t.Errorf("Instance.RunInput() error = %v", err) + } +} + +func TestInstance_RunOutput(t *testing.T) { + instance, _ := NewInstance() + instance.AddOutput(&mockOutputConverter{typ: "test"}) + + container := NewContainer() + err := instance.RunOutput(container) + if err != nil { + t.Errorf("Instance.RunOutput() error = %v", err) + } +} + +func TestInstance_InitConfigFromBytes(t *testing.T) { + // Save original state + originalInputCache := inputConfigCreatorCache + originalOutputCache := outputConfigCreatorCache + defer func() { + inputConfigCreatorCache = originalInputCache + outputConfigCreatorCache = originalOutputCache + }() + + // Reset caches + inputConfigCreatorCache = make(map[string]inputConfigCreator) + outputConfigCreatorCache = make(map[string]outputConfigCreator) + + // Register test creators + RegisterInputConfigCreator("test", func(action Action, data json.RawMessage) (InputConverter, error) { + return &mockInputConverter{typ: "test"}, nil + }) + RegisterOutputConfigCreator("test", func(action Action, data json.RawMessage) (OutputConverter, error) { + return &mockOutputConverter{typ: "test"}, nil + }) + + tests := []struct { + name string + config string + wantErr bool + }{ + { + name: "valid config", + config: `{ + "input": [{"type": "test", "action": "add", "args": {}}], + "output": [{"type": "test", "action": "output", "args": {}}] + }`, + wantErr: false, + }, + { + name: "config with comments", + config: `{ + // This is a comment + "input": [{"type": "test", "action": "add", "args": {}}], + "output": [{"type": "test", "action": "output", "args": {}}] + }`, + wantErr: false, + }, + { + name: "config with trailing comma", + config: `{ + "input": [{"type": "test", "action": "add", "args": {}}], + "output": [{"type": "test", "action": "output", "args": {}}], + }`, + wantErr: false, + }, + { + name: "invalid JSON", + config: `{invalid}`, + wantErr: true, + }, + { + name: "unknown input type", + config: `{ + "input": [{"type": "unknown", "action": "add", "args": {}}], + "output": [{"type": "test", "action": "output", "args": {}}] + }`, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + instance, _ := NewInstance() + err := instance.InitConfigFromBytes([]byte(tt.config)) + if (err != nil) != tt.wantErr { + t.Errorf("Instance.InitConfigFromBytes() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestInstance_InitConfig(t *testing.T) { + // Save original state + originalInputCache := inputConfigCreatorCache + originalOutputCache := outputConfigCreatorCache + defer func() { + inputConfigCreatorCache = originalInputCache + outputConfigCreatorCache = originalOutputCache + }() + + // Reset caches + inputConfigCreatorCache = make(map[string]inputConfigCreator) + outputConfigCreatorCache = make(map[string]outputConfigCreator) + + // Register test creators + RegisterInputConfigCreator("test", func(action Action, data json.RawMessage) (InputConverter, error) { + return &mockInputConverter{typ: "test"}, nil + }) + RegisterOutputConfigCreator("test", func(action Action, data json.RawMessage) (OutputConverter, error) { + return &mockOutputConverter{typ: "test"}, nil + }) + + configContent := `{ + "input": [{"type": "test", "action": "add", "args": {}}], + "output": [{"type": "test", "action": "output", "args": {}}] + }` + + t.Run("load from file", func(t *testing.T) { + // Create a temporary config file + tmpDir := t.TempDir() + configFile := filepath.Join(tmpDir, "config.json") + if err := os.WriteFile(configFile, []byte(configContent), 0644); err != nil { + t.Fatalf("Failed to create test config file: %v", err) + } + + instance, _ := NewInstance() + err := instance.InitConfig(configFile) + if err != nil { + t.Errorf("Instance.InitConfig() error = %v", err) + } + }) + + t.Run("load from HTTP URL", func(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte(configContent)) + })) + defer server.Close() + + instance, _ := NewInstance() + err := instance.InitConfig(server.URL) + if err != nil { + t.Errorf("Instance.InitConfig() with HTTP URL error = %v", err) + } + }) + + t.Run("load from HTTPS URL", func(t *testing.T) { + server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte(configContent)) + })) + defer server.Close() + + instance, _ := NewInstance() + // This may fail due to certificate issues in test, but we test the code path + instance.InitConfig(server.URL) + }) + + t.Run("non-existent file", func(t *testing.T) { + instance, _ := NewInstance() + err := instance.InitConfig("/nonexistent/config.json") + if err == nil { + t.Error("Instance.InitConfig() should return error for non-existent file") + } + }) + + t.Run("URL with whitespace", func(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte(configContent)) + })) + defer server.Close() + + instance, _ := NewInstance() + err := instance.InitConfig(" " + server.URL + " ") + if err != nil { + t.Errorf("Instance.InitConfig() with whitespace error = %v", err) + } + }) +} + +func TestInstance_RunInputError(t *testing.T) { + instance, _ := NewInstance() + + // Add an input converter that returns an error + instance.AddInput(&mockErrorInputConverter{}) + + container := NewContainer() + err := instance.RunInput(container) + if err == nil { + t.Error("Instance.RunInput() should return error when converter fails") + } +} + +func TestInstance_RunOutputError(t *testing.T) { + instance, _ := NewInstance() + + // Add an output converter that returns an error + instance.AddOutput(&mockErrorOutputConverter{}) + + container := NewContainer() + err := instance.RunOutput(container) + if err == nil { + t.Error("Instance.RunOutput() should return error when converter fails") + } +} + +func TestInstance_RunWithContainer(t *testing.T) { + // Save original state + originalInputCache := inputConfigCreatorCache + originalOutputCache := outputConfigCreatorCache + defer func() { + inputConfigCreatorCache = originalInputCache + outputConfigCreatorCache = originalOutputCache + }() + + // Reset caches + inputConfigCreatorCache = make(map[string]inputConfigCreator) + outputConfigCreatorCache = make(map[string]outputConfigCreator) + + // Register test creators + RegisterInputConfigCreator("test", func(action Action, data json.RawMessage) (InputConverter, error) { + return &mockInputConverter{typ: "test"}, nil + }) + RegisterOutputConfigCreator("test", func(action Action, data json.RawMessage) (OutputConverter, error) { + return &mockOutputConverter{typ: "test"}, nil + }) + + // Test full run with both input and output + instance, _ := NewInstance() + instance.AddInput(&mockInputConverter{typ: "test"}) + instance.AddOutput(&mockOutputConverter{typ: "test"}) + + err := instance.Run() + if err != nil { + t.Errorf("Instance.Run() error = %v", err) + } +} + +// Mock error converters +type mockErrorInputConverter struct{} + +func (m *mockErrorInputConverter) GetType() string { return "error" } +func (m *mockErrorInputConverter) GetAction() Action { return ActionAdd } +func (m *mockErrorInputConverter) GetDescription() string { return "error converter" } +func (m *mockErrorInputConverter) Input(c Container) (Container, error) { + return nil, ErrNotSupportedFormat +} + +type mockErrorOutputConverter struct{} + +func (m *mockErrorOutputConverter) GetType() string { return "error" } +func (m *mockErrorOutputConverter) GetAction() Action { return ActionOutput } +func (m *mockErrorOutputConverter) GetDescription() string { return "error converter" } +func (m *mockErrorOutputConverter) Output(c Container) error { + return ErrNotSupportedFormat +} |
