diff --git a/__snapshots__/test_complex_j_s_o_n_structure.snap b/__snapshots__/test_complex_j_s_o_n_structure.snap new file mode 100644 index 0000000..c67daa3 --- /dev/null +++ b/__snapshots__/test_complex_j_s_o_n_structure.snap @@ -0,0 +1,85 @@ +--- +title: Complex JSON Structure +test_name: TestComplexJSONStructure +file_path: +func_name: +version: 0.1.0 +--- +map[string]interface{}{ + "api": map[string]interface{}{ + "version": "2.0", + "endpoints": []interface{}{ + map[string]interface{}{ + "rate_limit": map[string]interface{}{ + "requests": 100.0, + "window": "1m", + }, + "responses": map[string]interface{}{ + "200": map[string]interface{}{ + "description": "Success", + "schema": map[string]interface{}{ + "type": "array", + "items": map[string]interface{}{ + "type": "object", + "properties": map[string]interface{}{ + "id": map[string]interface{}{ + "type": "integer", + }, + "name": map[string]interface{}{ + "type": "string", + }, + }, + }, + }, + }, + "401": map[string]interface{}{ + "description": "Unauthorized", + }, + }, + "path": "/users", + "method": "GET", + "auth_required": true, + }, + map[string]interface{}{ + "auth_required": true, + "rate_limit": map[string]interface{}{ + "requests": 50.0, + "window": "1m", + }, + "path": "/users/{id}", + "method": "POST", + }, + }, + "models": map[string]interface{}{ + "User": map[string]interface{}{ + "properties": map[string]interface{}{ + "created_at": map[string]interface{}{ + "type": "string", + "format": "date-time", + }, + "roles": map[string]interface{}{ + "type": "array", + "items": map[string]interface{}{ + "type": "string", + }, + }, + "id": map[string]interface{}{ + "type": "integer", + }, + "username": map[string]interface{}{ + "type": "string", + }, + "email": map[string]interface{}{ + "type": "string", + "format": "email", + }, + }, + "required": []interface{}{ + "id", + "username", + "email", + }, + }, + }, + }, +} diff --git a/__snapshots__/test_complex_nested_structure.snap b/__snapshots__/test_complex_nested_structure.snap new file mode 100644 index 0000000..880314f --- /dev/null +++ b/__snapshots__/test_complex_nested_structure.snap @@ -0,0 +1,88 @@ +--- +title: Complex Nested Structure +test_name: TestComplexNestedStructure +file_path: +func_name: +version: 0.1.0 +--- +freeze_test.Post{ + ID: 100, + Title: "Introduction to Go Snapshot Testing", + Content: "This is a comprehensive guide to snapshot testing in Go...", + Author: freeze_test.User{ + ID: 1, + Username: "john_doe", + Email: "john@example.com", + Active: true, + CreatedAt: time.Time{ + wall: 0x0, + ext: 63809375400, + loc: (*time.Location)(nil), + }, + Roles: []string{ + "admin", + "moderator", + "user", + }, + Metadata: map[string]interface{}{ + "language": "en", + "preferences": map[string]interface{}{ + "email_frequency": "weekly", + "notifications": true, + }, + "theme": "dark", + "notifications": true, + }, + }, + Tags: []string{ + "go", + "testing", + "snapshots", + "best-practices", + }, + Comments: []freeze_test.Comment{ + { + ID: 1, + Author: "alice", + Content: "Great post!", + CreatedAt: time.Time{ + wall: 0x0, + ext: 63810858120, + loc: (*time.Location)(nil), + }, + Replies: []freeze_test.Comment{ + { + ID: 2, + Author: "bob", + Content: "I agree!", + CreatedAt: time.Time{ + wall: 0x0, + ext: 63810863100, + loc: (*time.Location)(nil), + }, + Replies: []freeze_test.Comment{ + }, + }, + }, + }, + { + ID: 3, + Author: "charlie", + Content: "Thanks for sharing!", + CreatedAt: time.Time{ + wall: 0x0, + ext: 63810927000, + loc: (*time.Location)(nil), + }, + Replies: []freeze_test.Comment{ + }, + }, + }, + Likes: 42, + Published: true, + CreatedAt: time.Time{ + wall: 0x0, + ext: 63809802000, + loc: (*time.Location)(nil), + }, +} diff --git a/__snapshots__/test_deeply_nested_j_s_o_n.snap b/__snapshots__/test_deeply_nested_j_s_o_n.snap new file mode 100644 index 0000000..71b8853 --- /dev/null +++ b/__snapshots__/test_deeply_nested_j_s_o_n.snap @@ -0,0 +1,16 @@ +--- +title: Deeply Nested JSON +test_name: TestDeeplyNestedJSON +file_path: +func_name: +version: 0.1.0 +--- +map[string]interface{}{ + "L2": map[string]interface{}{ + "L3": map[string]interface{}{ + "L4": map[string]interface{}{ + "Value": "deep value", + }, + }, + }, +} diff --git a/__snapshots__/test_go_struct_marshalled_to_j_s_o_n.snap b/__snapshots__/test_go_struct_marshalled_to_j_s_o_n.snap new file mode 100644 index 0000000..e1da1fa --- /dev/null +++ b/__snapshots__/test_go_struct_marshalled_to_j_s_o_n.snap @@ -0,0 +1,24 @@ +--- +title: Go Struct Marshalled to JSON +test_name: TestGoStructMarshalledToJSON +file_path: +func_name: +version: 0.1.0 +--- +map[string]interface{}{ + "name": "Jane Smith", + "email": "jane@example.com", + "phone": "+1-555-0123", + "address": map[string]interface{}{ + "street": "456 Oak Ave", + "city": "San Francisco", + "zip": "94102", + }, + "tags": []interface{}{ + "vip", + "verified", + "premium", + }, + "active": true, + "created_at": "2023-06-15T14:30:00Z", +} diff --git a/__snapshots__/test_j_s_o_n_array_of_objects.snap b/__snapshots__/test_j_s_o_n_array_of_objects.snap new file mode 100644 index 0000000..c8df1c0 --- /dev/null +++ b/__snapshots__/test_j_s_o_n_array_of_objects.snap @@ -0,0 +1,35 @@ +--- +title: JSON Array of Objects +test_name: TestJSONArrayOfObjects +file_path: +func_name: +version: 0.1.0 +--- +[]interface{}{ + map[string]interface{}{ + "type": "user", + "id": 1.0, + "data": map[string]interface{}{ + "role": "admin", + "name": "Alice", + }, + }, + map[string]interface{}{ + "type": "post", + "id": 100.0, + "data": map[string]interface{}{ + "likes": 42.0, + "title": "First Post", + "author_id": 1.0, + }, + }, + map[string]interface{}{ + "type": "comment", + "id": 500.0, + "data": map[string]interface{}{ + "post_id": 100.0, + "content": "Great post!", + "author_id": 2.0, + }, + }, +} diff --git a/__snapshots__/test_j_s_o_n_numbers.snap b/__snapshots__/test_j_s_o_n_numbers.snap new file mode 100644 index 0000000..f861e73 --- /dev/null +++ b/__snapshots__/test_j_s_o_n_numbers.snap @@ -0,0 +1,25 @@ +--- +title: JSON Numbers +test_name: TestJSONNumbers +file_path: +func_name: +version: 0.1.0 +--- +map[string]interface{}{ + "floats": map[string]interface{}{ + "pi": 3.14159265359, + "scientific": 0.000123, + "negative_float": -42.5, + "small": 0.0001, + }, + "edge_cases": map[string]interface{}{ + "one": 1.0, + "minus_one": -1.0, + }, + "integers": map[string]interface{}{ + "zero": 0.0, + "positive": 42.0, + "negative": -100.0, + "large": 9.999999999999e+12.0, + }, +} diff --git a/__snapshots__/test_j_s_o_n_object.snap b/__snapshots__/test_j_s_o_n_object.snap new file mode 100644 index 0000000..9b54a48 --- /dev/null +++ b/__snapshots__/test_j_s_o_n_object.snap @@ -0,0 +1,27 @@ +--- +title: JSON Object +test_name: TestJSONObject +file_path: +func_name: +version: 0.1.0 +--- +map[string]interface{}{ + "user": map[string]interface{}{ + "roles": []interface{}{ + "user", + "admin", + }, + "created_at": "2023-01-15T10:30:00Z", + "id": 1.0, + "username": "john_doe", + "email": "john@example.com", + "profile": map[string]interface{}{ + "first_name": "John", + "last_name": "Doe", + "bio": "Software engineer", + "verified": true, + }, + }, + "status": "success", + "message": nil, +} diff --git a/__snapshots__/test_j_s_o_n_with_mixed_arrays.snap b/__snapshots__/test_j_s_o_n_with_mixed_arrays.snap new file mode 100644 index 0000000..090e891 --- /dev/null +++ b/__snapshots__/test_j_s_o_n_with_mixed_arrays.snap @@ -0,0 +1,55 @@ +--- +title: JSON with Mixed Arrays +test_name: TestJSONWithMixedArrays +file_path: +func_name: +version: 0.1.0 +--- +map[string]interface{}{ + "heterogeneous_array": []interface{}{ + "string", + 42.0, + 3.14, + true, + nil, + map[string]interface{}{ + "object": "value", + }, + []interface{}{ + 1.0, + 2.0, + 3.0, + }, + }, + "matrix": []interface{}{ + []interface{}{ + 1.0, + 2.0, + 3.0, + }, + []interface{}{ + 4.0, + 5.0, + 6.0, + }, + []interface{}{ + 7.0, + 8.0, + 9.0, + }, + }, + "object_array": []interface{}{ + map[string]interface{}{ + "id": 1.0, + "name": "Item 1", + }, + map[string]interface{}{ + "name": "Item 2", + "id": 2.0, + }, + map[string]interface{}{ + "name": "Item 3", + "id": 3.0, + }, + }, +} diff --git a/__snapshots__/test_j_s_o_n_with_special_characters.snap b/__snapshots__/test_j_s_o_n_with_special_characters.snap new file mode 100644 index 0000000..b6ef0ca --- /dev/null +++ b/__snapshots__/test_j_s_o_n_with_special_characters.snap @@ -0,0 +1,17 @@ +--- +title: JSON with Special Characters +test_name: TestJSONWithSpecialCharacters +file_path: +func_name: +version: 0.1.0 +--- +map[string]interface{}{ + "tabs": "col1\tcol2\tcol3", + "backslash": "path\\to\\file", + "english": "Hello, World!", + "unicode": "こんにちは 世界 🌍", + "emoji": "😀 😃 😄 😁 😆", + "special_chars": "!@#$%^&*()_+-=[]{}|;:,.<>?", + "escaped": "quotes: \"double\" and 'single'", + "newlines": "line1\nline2\rline3\r\nline4", +} diff --git a/__snapshots__/test_j_s_o_n_with_various_types.snap b/__snapshots__/test_j_s_o_n_with_various_types.snap new file mode 100644 index 0000000..e3008bd --- /dev/null +++ b/__snapshots__/test_j_s_o_n_with_various_types.snap @@ -0,0 +1,31 @@ +--- +title: JSON with Various Types +test_name: TestJSONWithVariousTypes +file_path: +func_name: +version: 0.1.0 +--- +map[string]interface{}{ + "float": 3.14159, + "boolean_true": true, + "null_value": nil, + "array": []interface{}{ + 1.0, + 2.0, + 3.0, + "four", + 5.5, + }, + "empty_array": []interface{}{ + }, + "empty_object": map[string]interface{}{ + }, + "escaped_string": "line1\nline2\ttab", + "string": "hello world", + "integer": 42.0, + "boolean_false": false, + "object": map[string]interface{}{ + "nested": "value", + "count": 10.0, + }, +} diff --git a/__snapshots__/test_large_j_s_o_n.snap b/__snapshots__/test_large_j_s_o_n.snap new file mode 100644 index 0000000..acb1a88 --- /dev/null +++ b/__snapshots__/test_large_j_s_o_n.snap @@ -0,0 +1,55 @@ +--- +title: Large JSON Structure +test_name: TestLargeJSON +file_path: +func_name: +version: 0.1.0 +--- +map[string]interface{}{ + "total": 1179.97, + "status": "shipped", + "created_at": "2023-01-28T14:30:00Z", + "shipped_at": "2023-02-01T10:00:00Z", + "delivered_at": nil, + "id": 1001.0, + "customer_id": 42.0, + "products": []interface{}{ + map[string]interface{}{ + "price": 999.99, + "stock": 5.0, + "in_stock": true, + "tags": []interface{}{ + "electronics", + "computers", + "laptops", + }, + "id": 1.0, + "name": "Laptop", + "description": "High-performance laptop", + }, + map[string]interface{}{ + "name": "Mouse", + "description": "Wireless mouse", + "price": 29.99, + "stock": 50.0, + "in_stock": true, + "tags": []interface{}{ + "electronics", + "accessories", + }, + "id": 2.0, + }, + map[string]interface{}{ + "description": "Mechanical keyboard", + "price": 149.99, + "stock": 0.0, + "in_stock": false, + "tags": []interface{}{ + "electronics", + "accessories", + }, + "id": 3.0, + "name": "Keyboard", + }, + }, +} diff --git a/__snapshots__/test_multiple_complex_structures.snap b/__snapshots__/test_multiple_complex_structures.snap new file mode 100644 index 0000000..af615ac --- /dev/null +++ b/__snapshots__/test_multiple_complex_structures.snap @@ -0,0 +1,65 @@ +--- +title: Multiple Complex Structures +test_name: TestMultipleComplexStructures +file_path: +func_name: +version: 0.1.0 +--- +[]freeze_test.User{ + { + ID: 1, + Username: "alice", + Email: "alice@example.com", + Active: true, + CreatedAt: time.Time{ + wall: 0x0, + ext: 0, + loc: (*time.Location)(nil), + }, + Roles: []string{ + "user", + "moderator", + }, + Metadata: map[string]interface{}{ + "verified": true, + "badge": "verified", + }, + }, + { + ID: 2, + Username: "bob", + Email: "bob@example.com", + Active: false, + CreatedAt: time.Time{ + wall: 0x0, + ext: 0, + loc: (*time.Location)(nil), + }, + Roles: []string{ + "user", + }, + Metadata: map[string]interface{}{ + "verified": false, + "avatar": "https://example.com/bob.jpg", + }, + }, + { + ID: 3, + Username: "charlie", + Email: "charlie@example.com", + Active: true, + CreatedAt: time.Time{ + wall: 0x0, + ext: 0, + loc: (*time.Location)(nil), + }, + Roles: []string{ + "user", + "admin", + }, + Metadata: map[string]interface{}{ + "verified": true, + "account_age_days": 365, + }, + }, +} diff --git a/__snapshots__/test_nested_maps_and_slices.snap b/__snapshots__/test_nested_maps_and_slices.snap new file mode 100644 index 0000000..a68f5db --- /dev/null +++ b/__snapshots__/test_nested_maps_and_slices.snap @@ -0,0 +1,52 @@ +--- +title: Nested Maps and Slices +test_name: TestNestedMapsAndSlices +file_path: +func_name: +version: 0.1.0 +--- +map[string]interface{}{ + "users": map[string]interface{}{ + "active": []map[string]interface{}{ + { + "id": 1, + "name": "Alice", + "verified": true, + }, + { + "id": 2, + "name": "Bob", + "verified": false, + }, + }, + "inactive": []map[string]interface{}{ + { + "id": 3, + "name": "Charlie", + }, + }, + }, + "posts": map[string]interface{}{ + "published": 42, + "drafts": 5, + "categories": []string{ + "tech", + "lifestyle", + "news", + }, + }, + "stats": map[string]interface{}{ + "daily": map[string]interface{}{ + "views": 1500, + "clicks": 320, + "conversions": map[string]interface{}{ + "total": 45, + "by_source": map[string]int{ + "paid": 15, + "referral": 5, + "organic": 25, + }, + }, + }, + }, +} diff --git a/__snapshots__/test_structure_with_empty_values.snap b/__snapshots__/test_structure_with_empty_values.snap new file mode 100644 index 0000000..84e1db3 --- /dev/null +++ b/__snapshots__/test_structure_with_empty_values.snap @@ -0,0 +1,39 @@ +--- +title: Structure with Empty Values +test_name: TestStructureWithEmptyValues +file_path: +func_name: +version: 0.1.0 +--- +[]freeze_test.Container{ + { + Items: []string{ + }, + Tags: map[string]string{ + }, + OptionalID: (*int)(nil), + Count: 0, + Active: false, + }, + { + Items: []string(nil), + Tags: map[string]string(nil), + OptionalID: (*int)(nil), + Count: 0, + Active: true, + }, + { + Items: []string{ + "a", + "b", + "c", + }, + Tags: map[string]string{ + "type": "test", + "env": "dev", + }, + OptionalID: &int(42), + Count: 3, + Active: true, + }, +} diff --git a/__snapshots__/test_structure_with_interface.snap b/__snapshots__/test_structure_with_interface.snap new file mode 100644 index 0000000..5f38d02 --- /dev/null +++ b/__snapshots__/test_structure_with_interface.snap @@ -0,0 +1,77 @@ +--- +title: Structure with Interface Fields +test_name: TestStructureWithInterface +file_path: +func_name: +version: 0.1.0 +--- +[]freeze_test.Response{ + { + Status: "success", + Message: "User retrieved", + Data: freeze_test.User{ + ID: 1, + Username: "john", + Email: "john@example.com", + Active: true, + CreatedAt: time.Time{ + wall: 0x0, + ext: 0, + loc: (*time.Location)(nil), + }, + Roles: []string(nil), + Metadata: map[string]interface{}(nil), + }, + Meta: map[string]interface{}{ + "request_id": "req-123", + "timestamp": "2023-01-20T10:30:00Z", + }, + }, + { + Status: "error", + Message: "User not found", + Data: nil, + Meta: map[string]interface{}{ + "error_code": 404, + "error_type": "NOT_FOUND", + }, + }, + { + Status: "success", + Message: "Posts retrieved", + Data: []freeze_test.Post{ + { + ID: 1, + Title: "First Post", + Content: "", + Author: freeze_test.User{ + ID: 0, + Username: "", + Email: "", + Active: false, + CreatedAt: time.Time{ + wall: 0x0, + ext: 0, + loc: (*time.Location)(nil), + }, + Roles: []string(nil), + Metadata: map[string]interface{}(nil), + }, + Tags: []string(nil), + Comments: []freeze_test.Comment(nil), + Likes: 0, + Published: true, + CreatedAt: time.Time{ + wall: 0x0, + ext: 0, + loc: (*time.Location)(nil), + }, + }, + }, + Meta: map[string]interface{}{ + "total_count": 10, + "page": 1, + "per_page": 20, + }, + }, +} diff --git a/__snapshots__/test_structure_with_pointers.snap b/__snapshots__/test_structure_with_pointers.snap new file mode 100644 index 0000000..f72e580 --- /dev/null +++ b/__snapshots__/test_structure_with_pointers.snap @@ -0,0 +1,28 @@ +--- +title: Structure with Pointers +test_name: TestStructureWithPointers +file_path: +func_name: +version: 0.1.0 +--- +freeze_test.Person{ + Name: "John", + Age: 35, + Address: &freeze_test.Address{ + Street: "123 Main St", + City: "Boston", + Zip: "02101", + }, + Manager: &freeze_test.Person{ + Name: "Jane", + Age: 30, + Address: (*freeze_test.Address)(), + Manager: (*freeze_test.Person)(nil), + Friends: []*freeze_test.Person(nil), + Email: &string("jane@example.com"), + }, + Friends: []*freeze_test.Person{ + (*freeze_test.Person)(), + }, + Email: (*string)(nil), +} diff --git a/freeze_test.go b/freeze_test.go index 647ade4..26c81d4 100644 --- a/freeze_test.go +++ b/freeze_test.go @@ -1,10 +1,12 @@ package freeze_test import ( + "encoding/json" "os" "path/filepath" "strings" "testing" + "time" "github.com/ptdewey/freeze" "github.com/ptdewey/freeze/internal/api" @@ -207,3 +209,742 @@ func cleanupTestSnapshots(t *testing.T) { snapshotDir := filepath.Join(cwd, "__snapshots__") _ = os.RemoveAll(snapshotDir) } + +// ============================================================================ +// COMPLEX GO STRUCTURES TESTS +// ============================================================================ + +// User represents a user in a system +type User struct { + ID int + Username string + Email string + Active bool + CreatedAt time.Time + Roles []string + Metadata map[string]interface{} +} + +// Post represents a blog post +type Post struct { + ID int + Title string + Content string + Author User + Tags []string + Comments []Comment + Likes int + Published bool + CreatedAt time.Time +} + +// Comment represents a comment on a post +type Comment struct { + ID int + Author string + Content string + CreatedAt time.Time + Replies []Comment +} + +// TestComplexNestedStructure tests snapshot with deeply nested Go structures +func TestComplexNestedStructure(t *testing.T) { + user := User{ + ID: 1, + Username: "john_doe", + Email: "john@example.com", + Active: true, + CreatedAt: time.Date(2023, 1, 15, 10, 30, 0, 0, time.UTC), + Roles: []string{"admin", "moderator", "user"}, + Metadata: map[string]interface{}{ + "theme": "dark", + "notifications": true, + "language": "en", + "preferences": map[string]interface{}{ + "email_frequency": "weekly", + "notifications": true, + }, + }, + } + + comments := []Comment{ + { + ID: 1, + Author: "alice", + Content: "Great post!", + CreatedAt: time.Date(2023, 2, 1, 14, 22, 0, 0, time.UTC), + Replies: []Comment{ + { + ID: 2, + Author: "bob", + Content: "I agree!", + CreatedAt: time.Date(2023, 2, 1, 15, 45, 0, 0, time.UTC), + Replies: []Comment{}, + }, + }, + }, + { + ID: 3, + Author: "charlie", + Content: "Thanks for sharing!", + CreatedAt: time.Date(2023, 2, 2, 9, 30, 0, 0, time.UTC), + Replies: []Comment{}, + }, + } + + post := Post{ + ID: 100, + Title: "Introduction to Go Snapshot Testing", + Content: "This is a comprehensive guide to snapshot testing in Go...", + Author: user, + Tags: []string{"go", "testing", "snapshots", "best-practices"}, + Comments: comments, + Likes: 42, + Published: true, + CreatedAt: time.Date(2023, 1, 20, 9, 0, 0, 0, time.UTC), + } + + freeze.Snap(t, "Complex Nested Structure", post) +} + +// TestMultipleComplexStructures tests snapshot with multiple complex structures +func TestMultipleComplexStructures(t *testing.T) { + users := []User{ + { + ID: 1, + Username: "alice", + Email: "alice@example.com", + Active: true, + Roles: []string{"user", "moderator"}, + Metadata: map[string]interface{}{ + "verified": true, + "badge": "verified", + }, + }, + { + ID: 2, + Username: "bob", + Email: "bob@example.com", + Active: false, + Roles: []string{"user"}, + Metadata: map[string]interface{}{ + "verified": false, + "avatar": "https://example.com/bob.jpg", + }, + }, + { + ID: 3, + Username: "charlie", + Email: "charlie@example.com", + Active: true, + Roles: []string{"user", "admin"}, + Metadata: map[string]interface{}{ + "verified": true, + "account_age_days": 365, + }, + }, + } + + freeze.Snap(t, "Multiple Complex Structures", users) +} + +// TestStructureWithInterface tests structures containing interface{} fields +func TestStructureWithInterface(t *testing.T) { + type Response struct { + Status string + Message string + Data interface{} + Meta map[string]interface{} + } + + responses := []Response{ + { + Status: "success", + Message: "User retrieved", + Data: User{ + ID: 1, + Username: "john", + Email: "john@example.com", + Active: true, + }, + Meta: map[string]interface{}{ + "request_id": "req-123", + "timestamp": "2023-01-20T10:30:00Z", + }, + }, + { + Status: "error", + Message: "User not found", + Data: nil, + Meta: map[string]interface{}{ + "error_code": 404, + "error_type": "NOT_FOUND", + }, + }, + { + Status: "success", + Message: "Posts retrieved", + Data: []Post{ + { + ID: 1, + Title: "First Post", + Published: true, + }, + }, + Meta: map[string]interface{}{ + "total_count": 10, + "page": 1, + "per_page": 20, + }, + }, + } + + freeze.Snap(t, "Structure with Interface Fields", responses) +} + +// TestNestedMapsAndSlices tests complex nested maps and slices +func TestNestedMapsAndSlices(t *testing.T) { + complexData := map[string]interface{}{ + "users": map[string]interface{}{ + "active": []map[string]interface{}{ + { + "id": 1, + "name": "Alice", + "verified": true, + }, + { + "id": 2, + "name": "Bob", + "verified": false, + }, + }, + "inactive": []map[string]interface{}{ + { + "id": 3, + "name": "Charlie", + }, + }, + }, + "posts": map[string]interface{}{ + "published": 42, + "drafts": 5, + "categories": []string{"tech", "lifestyle", "news"}, + }, + "stats": map[string]interface{}{ + "daily": map[string]interface{}{ + "views": 1500, + "clicks": 320, + "conversions": map[string]interface{}{ + "total": 45, + "by_source": map[string]int{ + "organic": 25, + "paid": 15, + "referral": 5, + }, + }, + }, + }, + } + + freeze.Snap(t, "Nested Maps and Slices", complexData) +} + +// TestStructureWithPointers tests structures with pointer fields +func TestStructureWithPointers(t *testing.T) { + type Address struct { + Street string + City string + Zip string + } + + type Person struct { + Name string + Age int + Address *Address + Manager *Person + Friends []*Person + Email *string + } + + addr1 := &Address{ + Street: "123 Main St", + City: "Boston", + Zip: "02101", + } + + email := "jane@example.com" + + person1 := Person{ + Name: "Jane", + Age: 30, + Address: addr1, + Email: &email, + } + + person2 := Person{ + Name: "John", + Age: 35, + Address: addr1, + Manager: &person1, + Friends: []*Person{&person1}, + } + + freeze.Snap(t, "Structure with Pointers", person2) +} + +// TestStructureWithEmptyValues tests structures with empty slices, maps, nil values +func TestStructureWithEmptyValues(t *testing.T) { + type Container struct { + Items []string + Tags map[string]string + OptionalID *int + Count int + Active bool + } + + containers := []Container{ + { + Items: []string{}, + Tags: map[string]string{}, + OptionalID: nil, + Count: 0, + Active: false, + }, + { + Items: nil, + Tags: nil, + OptionalID: nil, + Count: 0, + Active: true, + }, + { + Items: []string{"a", "b", "c"}, + Tags: map[string]string{"type": "test", "env": "dev"}, + OptionalID: intPtr(42), + Count: 3, + Active: true, + }, + } + + freeze.Snap(t, "Structure with Empty Values", containers) +} + +// ============================================================================ +// JSON OBJECT TESTS +// ============================================================================ + +// TestJSONObject tests snapshot with JSON objects +func TestJSONObject(t *testing.T) { + jsonStr := `{ + "user": { + "id": 1, + "username": "john_doe", + "email": "john@example.com", + "profile": { + "first_name": "John", + "last_name": "Doe", + "bio": "Software engineer", + "verified": true + }, + "roles": ["user", "admin"], + "created_at": "2023-01-15T10:30:00Z" + }, + "status": "success", + "message": null + }` + + var data interface{} + if err := json.Unmarshal([]byte(jsonStr), &data); err != nil { + t.Fatalf("failed to unmarshal json: %v", err) + } + + freeze.Snap(t, "JSON Object", data) +} + +// TestComplexJSONStructure tests complex nested JSON structures +func TestComplexJSONStructure(t *testing.T) { + jsonStr := `{ + "api": { + "version": "2.0", + "endpoints": [ + { + "path": "/users", + "method": "GET", + "auth_required": true, + "rate_limit": { + "requests": 100, + "window": "1m" + }, + "responses": { + "200": { + "description": "Success", + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": {"type": "integer"}, + "name": {"type": "string"} + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + }, + { + "path": "/users/{id}", + "method": "POST", + "auth_required": true, + "rate_limit": { + "requests": 50, + "window": "1m" + } + } + ], + "models": { + "User": { + "properties": { + "id": {"type": "integer"}, + "username": {"type": "string"}, + "email": {"type": "string", "format": "email"}, + "created_at": {"type": "string", "format": "date-time"}, + "roles": { + "type": "array", + "items": {"type": "string"} + } + }, + "required": ["id", "username", "email"] + } + } + } + }` + + var data interface{} + if err := json.Unmarshal([]byte(jsonStr), &data); err != nil { + t.Fatalf("failed to unmarshal json: %v", err) + } + + freeze.Snap(t, "Complex JSON Structure", data) +} + +// TestJSONArrayOfObjects tests JSON arrays with multiple object types +func TestJSONArrayOfObjects(t *testing.T) { + jsonStr := `[ + { + "type": "user", + "id": 1, + "data": { + "name": "Alice", + "role": "admin" + } + }, + { + "type": "post", + "id": 100, + "data": { + "title": "First Post", + "author_id": 1, + "likes": 42 + } + }, + { + "type": "comment", + "id": 500, + "data": { + "content": "Great post!", + "author_id": 2, + "post_id": 100 + } + } + ]` + + var data interface{} + if err := json.Unmarshal([]byte(jsonStr), &data); err != nil { + t.Fatalf("failed to unmarshal json: %v", err) + } + + freeze.Snap(t, "JSON Array of Objects", data) +} + +// TestJSONWithVariousTypes tests JSON with various data types +func TestJSONWithVariousTypes(t *testing.T) { + jsonStr := `{ + "string": "hello world", + "integer": 42, + "float": 3.14159, + "boolean_true": true, + "boolean_false": false, + "null_value": null, + "array": [1, 2, 3, "four", 5.5], + "object": { + "nested": "value", + "count": 10 + }, + "empty_array": [], + "empty_object": {}, + "escaped_string": "line1\nline2\ttab" + }` + + var data interface{} + if err := json.Unmarshal([]byte(jsonStr), &data); err != nil { + t.Fatalf("failed to unmarshal json: %v", err) + } + + freeze.Snap(t, "JSON with Various Types", data) +} + +// TestJSONNumbers tests JSON with various number formats +func TestJSONNumbers(t *testing.T) { + jsonStr := `{ + "integers": { + "zero": 0, + "positive": 42, + "negative": -100, + "large": 9999999999999 + }, + "floats": { + "small": 0.0001, + "pi": 3.14159265359, + "scientific": 1.23e-4, + "negative_float": -42.5 + }, + "edge_cases": { + "one": 1, + "minus_one": -1 + } + }` + + var data interface{} + if err := json.Unmarshal([]byte(jsonStr), &data); err != nil { + t.Fatalf("failed to unmarshal json: %v", err) + } + + freeze.Snap(t, "JSON Numbers", data) +} + +// TestJSONWithSpecialCharacters tests JSON with special characters and unicode +func TestJSONWithSpecialCharacters(t *testing.T) { + jsonStr := `{ + "english": "Hello, World!", + "unicode": "こんにちは 世界 🌍", + "emoji": "😀 😃 😄 😁 😆", + "special_chars": "!@#$%^&*()_+-=[]{}|;:,.<>?", + "escaped": "quotes: \"double\" and 'single'", + "newlines": "line1\nline2\rline3\r\nline4", + "tabs": "col1\tcol2\tcol3", + "backslash": "path\\to\\file" + }` + + var data interface{} + if err := json.Unmarshal([]byte(jsonStr), &data); err != nil { + t.Fatalf("failed to unmarshal json: %v", err) + } + + freeze.Snap(t, "JSON with Special Characters", data) +} + +// TestGoStructMarshalledToJSON tests Go struct marshalled to JSON +func TestGoStructMarshalledToJSON(t *testing.T) { + type Address struct { + Street string `json:"street"` + City string `json:"city"` + Zip string `json:"zip"` + } + + type Contact struct { + Name string `json:"name"` + Email string `json:"email"` + Phone string `json:"phone"` + Address Address `json:"address"` + Tags []string `json:"tags"` + Active bool `json:"active"` + CreatedAt time.Time `json:"created_at"` + } + + contact := Contact{ + Name: "Jane Smith", + Email: "jane@example.com", + Phone: "+1-555-0123", + Address: Address{ + Street: "456 Oak Ave", + City: "San Francisco", + Zip: "94102", + }, + Tags: []string{"vip", "verified", "premium"}, + Active: true, + CreatedAt: time.Date(2023, 6, 15, 14, 30, 0, 0, time.UTC), + } + + jsonBytes, err := json.MarshalIndent(contact, "", " ") + if err != nil { + t.Fatalf("failed to marshal json: %v", err) + } + + var data interface{} + if err := json.Unmarshal(jsonBytes, &data); err != nil { + t.Fatalf("failed to unmarshal json: %v", err) + } + + freeze.Snap(t, "Go Struct Marshalled to JSON", data) +} + +// TestDeeplyNestedJSON tests deeply nested JSON structure +func TestDeeplyNestedJSON(t *testing.T) { + type Level4 struct { + Value string + } + + type Level3 struct { + L4 Level4 + } + + type Level2 struct { + L3 Level3 + } + + type Level1 struct { + L2 Level2 + } + + l1 := Level1{ + L2: Level2{ + L3: Level3{ + L4: Level4{ + Value: "deep value", + }, + }, + }, + } + + jsonBytes, err := json.MarshalIndent(l1, "", " ") + if err != nil { + t.Fatalf("failed to marshal json: %v", err) + } + + var data interface{} + if err := json.Unmarshal(jsonBytes, &data); err != nil { + t.Fatalf("failed to unmarshal json: %v", err) + } + + freeze.Snap(t, "Deeply Nested JSON", data) +} + +// TestLargeJSON tests larger JSON structure with many fields +func TestLargeJSON(t *testing.T) { + type Product struct { + ID int `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Price float64 `json:"price"` + Stock int `json:"stock"` + InStock bool `json:"in_stock"` + Tags []string `json:"tags"` + } + + type Order struct { + ID int `json:"id"` + CustomerID int `json:"customer_id"` + Products []Product `json:"products"` + Total float64 `json:"total"` + Status string `json:"status"` + CreatedAt time.Time `json:"created_at"` + ShippedAt *time.Time `json:"shipped_at"` + DeliveredAt *time.Time `json:"delivered_at"` + } + + shippedTime := time.Date(2023, 2, 1, 10, 0, 0, 0, time.UTC) + + order := Order{ + ID: 1001, + CustomerID: 42, + Products: []Product{ + { + ID: 1, + Name: "Laptop", + Description: "High-performance laptop", + Price: 999.99, + Stock: 5, + InStock: true, + Tags: []string{"electronics", "computers", "laptops"}, + }, + { + ID: 2, + Name: "Mouse", + Description: "Wireless mouse", + Price: 29.99, + Stock: 50, + InStock: true, + Tags: []string{"electronics", "accessories"}, + }, + { + ID: 3, + Name: "Keyboard", + Description: "Mechanical keyboard", + Price: 149.99, + Stock: 0, + InStock: false, + Tags: []string{"electronics", "accessories"}, + }, + }, + Total: 1179.97, + Status: "shipped", + CreatedAt: time.Date(2023, 1, 28, 14, 30, 0, 0, time.UTC), + ShippedAt: &shippedTime, + DeliveredAt: nil, + } + + jsonBytes, err := json.MarshalIndent(order, "", " ") + if err != nil { + t.Fatalf("failed to marshal json: %v", err) + } + + var data interface{} + if err := json.Unmarshal(jsonBytes, &data); err != nil { + t.Fatalf("failed to unmarshal json: %v", err) + } + + freeze.Snap(t, "Large JSON Structure", data) +} + +// TestJSONWithMixedArrays tests JSON with arrays containing different types +func TestJSONWithMixedArrays(t *testing.T) { + jsonStr := `{ + "heterogeneous_array": [ + "string", + 42, + 3.14, + true, + null, + {"object": "value"}, + [1, 2, 3] + ], + "matrix": [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9] + ], + "object_array": [ + {"id": 1, "name": "Item 1"}, + {"id": 2, "name": "Item 2"}, + {"id": 3, "name": "Item 3"} + ] + }` + + var data interface{} + if err := json.Unmarshal([]byte(jsonStr), &data); err != nil { + t.Fatalf("failed to unmarshal json: %v", err) + } + + freeze.Snap(t, "JSON with Mixed Arrays", data) +} + +// ============================================================================ +// HELPER FUNCTIONS +// ============================================================================ + +func intPtr(i int) *int { + return &i +}