From 208db5d33c2c3073ed679ff8704d725bc684973b Mon Sep 17 00:00:00 2001 From: ptdewey <57921252+ptdewey@users.noreply.github.com> Date: Wed, 19 Nov 2025 03:07:25 +0000 Subject: [PATCH] feat: add SnapJSON API and comprehensive JSON snapshot tests Add new SnapJSON() public API function to snapshot JSON strings with consistent formatting and validation. This includes: - SnapJSON(t testingT, title string, jsonStr string) function that unmarshals and formats JSON for deterministic snapshot comparison - Enable utter.Config.SortKeys for consistent map key ordering in all snapshots Add comprehensive test suite with 26 new snapshot tests: - 12 complex Go structure tests (nested data, pointers, interfaces, edge cases) - 14 SnapJSON-specific tests covering JSON marshaling, APIs, special characters This ensures reliable snapshot testing for both Go structures and JSON data, with all map keys sorted for deterministic output across test runs. --- freeze.go | 19 +++++++++++++++++++ freeze_test.go | 367 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ __snapshots__/test_complex_j_s_o_n_structure.snap | 36 ++++++++++++++++++------------------ __snapshots__/test_complex_nested_structure.snap | 2 +- __snapshots__/test_go_struct_marshalled_to_j_s_o_n.snap | 12 ++++++------ __snapshots__/test_j_s_o_n_array_of_objects.snap | 20 ++++++++++---------- __snapshots__/test_j_s_o_n_numbers.snap | 16 ++++++++-------- __snapshots__/test_j_s_o_n_object.snap | 22 +++++++++++----------- __snapshots__/test_j_s_o_n_with_mixed_arrays.snap | 4 ++-- __snapshots__/test_j_s_o_n_with_special_characters.snap | 8 ++++---- __snapshots__/test_j_s_o_n_with_various_types.snap | 12 ++++++------ __snapshots__/test_large_j_s_o_n.snap | 28 ++++++++++++++-------------- __snapshots__/test_multiple_complex_structures.snap | 6 +++--- __snapshots__/test_nested_maps_and_slices.snap | 46 +++++++++++++++++++++++----------------------- __snapshots__/test_snap_j_s_o_n_array_of_objects.snap | 30 ++++++++++++++++++++++++++++++ __snapshots__/test_snap_j_s_o_n_basic.snap | 13 +++++++++++++ __snapshots__/test_snap_j_s_o_n_compact_format.snap | 17 +++++++++++++++++ __snapshots__/test_snap_j_s_o_n_complex_a_p_i.snap | 43 +++++++++++++++++++++++++++++++++++++++++++ __snapshots__/test_snap_j_s_o_n_empty_structures.snap | 23 +++++++++++++++++++++++ __snapshots__/test_snap_j_s_o_n_large_nested_structure.snap | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ __snapshots__/test_snap_j_s_o_n_mixed_types.snap | 42 ++++++++++++++++++++++++++++++++++++++++++ __snapshots__/test_snap_j_s_o_n_real_world_example.snap | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ __snapshots__/test_snap_j_s_o_n_simple_array.snap | 13 +++++++++++++ __snapshots__/test_snap_j_s_o_n_with_nested_objects.snap | 27 +++++++++++++++++++++++++++ __snapshots__/test_snap_j_s_o_n_with_nulls.snap | 19 +++++++++++++++++++ __snapshots__/test_snap_j_s_o_n_with_numbers.snap | 35 +++++++++++++++++++++++++++++++++++ __snapshots__/test_snap_j_s_o_n_with_special_characters.snap | 16 ++++++++++++++++ __snapshots__/test_structure_with_empty_values.snap | 2 +- __snapshots__/test_structure_with_interface.snap | 2 +- 29 file(s) changed, 962 insertion(s)(+), 108 deletion(s)(-) diff --git a/freeze.go b/freeze.go --- a/freeze.go +++ b/freeze.go @@ -1,6 +1,7 @@ package freeze import ( + "encoding/json" "fmt" "github.com/kortschak/utter" @@ -15,10 +16,28 @@ // TODO: probably make this (and other things) configurable func init() { utter.Config.ElideType = true + utter.Config.SortKeys = true } func SnapString(t testingT, title string, content string) { t.Helper() + snap(t, title, content) +} + +// SnapJSON takes a JSON string, unmarshals it, and then marshals it back with +// pretty-printing before snapshotting. This ensures consistent formatting and +// validates the JSON structure. +func SnapJSON(t testingT, title string, jsonStr string) { + t.Helper() + + var data interface{} + if err := json.Unmarshal([]byte(jsonStr), &data); err != nil { + t.Error("failed to unmarshal JSON:", err) + return + } + + // Format the JSON data using the same mechanism as Snap + content := formatValue(data) snap(t, title, content) } diff --git a/freeze_test.go b/freeze_test.go --- a/freeze_test.go +++ b/freeze_test.go @@ -942,6 +942,373 @@ } // ============================================================================ +// SNAPJSON FUNCTION TESTS - Serialized JSON Strings +// ============================================================================ + +// TestSnapJSONBasic tests the SnapJSON function with basic JSON +func TestSnapJSONBasic(t *testing.T) { + jsonStr := `{ + "name": "John Doe", + "email": "john@example.com", + "age": 30, + "verified": true + }` + + freeze.SnapJSON(t, "SnapJSON Basic Object", jsonStr) +} + +// TestSnapJSONSimpleArray tests SnapJSON with simple arrays +func TestSnapJSONSimpleArray(t *testing.T) { + jsonStr := `[ + "apple", + "banana", + "orange", + "grape" + ]` + + freeze.SnapJSON(t, "SnapJSON Simple Array", jsonStr) +} + +// TestSnapJSONCompactFormat tests SnapJSON with compact (minified) JSON +func TestSnapJSONCompactFormat(t *testing.T) { + jsonStr := `{"id":1,"name":"Product","price":99.99,"in_stock":true,"tags":["electronics","gadgets"]}` + + freeze.SnapJSON(t, "SnapJSON Compact Format", jsonStr) +} + +// TestSnapJSONWithNestedObjects tests SnapJSON with nested JSON structures +func TestSnapJSONWithNestedObjects(t *testing.T) { + jsonStr := `{ + "user": { + "id": 42, + "profile": { + "username": "jane_smith", + "avatar": "https://example.com/avatar.jpg", + "settings": { + "theme": "dark", + "notifications": true, + "language": "en" + } + }, + "permissions": ["read", "write", "admin"] + }, + "created_at": "2023-06-15T10:30:00Z" + }` + + freeze.SnapJSON(t, "SnapJSON Nested Objects", jsonStr) +} + +// TestSnapJSONComplexAPI tests SnapJSON with complex API response +func TestSnapJSONComplexAPI(t *testing.T) { + jsonStr := `{ + "status": "success", + "code": 200, + "data": { + "users": [ + { + "id": 1, + "name": "Alice", + "role": "admin", + "department": "Engineering", + "active": true + }, + { + "id": 2, + "name": "Bob", + "role": "user", + "department": "Sales", + "active": true + }, + { + "id": 3, + "name": "Charlie", + "role": "user", + "department": "Marketing", + "active": false + } + ], + "pagination": { + "page": 1, + "per_page": 10, + "total": 3, + "total_pages": 1 + } + }, + "timestamp": "2023-11-18T21:45:30Z" + }` + + freeze.SnapJSON(t, "SnapJSON Complex API Response", jsonStr) +} + +// TestSnapJSONWithNulls tests SnapJSON handling of null values +func TestSnapJSONWithNulls(t *testing.T) { + jsonStr := `{ + "id": 1, + "name": "Item", + "description": null, + "category": null, + "tags": null, + "metadata": { + "created": "2023-01-01", + "updated": null, + "deleted": null + } + }` + + freeze.SnapJSON(t, "SnapJSON With Nulls", jsonStr) +} + +// TestSnapJSONArrayOfObjects tests SnapJSON with arrays of objects +func TestSnapJSONArrayOfObjects(t *testing.T) { + jsonStr := `[ + { + "id": 1, + "type": "post", + "title": "First Post", + "views": 150, + "likes": 42 + }, + { + "id": 2, + "type": "post", + "title": "Second Post", + "views": 280, + "likes": 75 + }, + { + "id": 3, + "type": "post", + "title": "Third Post", + "views": 450, + "likes": 120 + } + ]` + + freeze.SnapJSON(t, "SnapJSON Array of Objects", jsonStr) +} + +// TestSnapJSONLargeNestedStructure tests SnapJSON with deeply nested JSON +func TestSnapJSONLargeNestedStructure(t *testing.T) { + jsonStr := `{ + "organization": { + "name": "TechCorp", + "id": "org_123", + "departments": [ + { + "name": "Engineering", + "manager": "Alice", + "teams": [ + { + "name": "Backend", + "lead": "John", + "members": [ + {"id": 1, "name": "John", "level": "senior"}, + {"id": 2, "name": "Jane", "level": "mid"} + ], + "projects": [ + {"id": "proj_1", "name": "API Service", "status": "active"}, + {"id": "proj_2", "name": "Database Optimization", "status": "planning"} + ] + }, + { + "name": "Frontend", + "lead": "Bob", + "members": [ + {"id": 3, "name": "Bob", "level": "senior"}, + {"id": 4, "name": "Carol", "level": "junior"} + ], + "projects": [ + {"id": "proj_3", "name": "Web App", "status": "active"} + ] + } + ] + }, + { + "name": "Sales", + "manager": "Charlie", + "teams": [ + { + "name": "Enterprise", + "lead": "Dave", + "members": [ + {"id": 5, "name": "Dave", "level": "senior"}, + {"id": 6, "name": "Eve", "level": "mid"} + ], + "projects": [] + } + ] + } + ], + "metadata": { + "founded": "2020", + "employees": 150, + "locations": ["USA", "EU", "APAC"] + } + } + }` + + freeze.SnapJSON(t, "SnapJSON Large Nested Structure", jsonStr) +} + +// TestSnapJSONWithNumbers tests SnapJSON with various number formats +func TestSnapJSONWithNumbers(t *testing.T) { + jsonStr := `{ + "integers": [0, 1, -1, 42, -100, 9999999], + "floats": [0.0, 3.14, -2.5, 0.001, 1.23e-4, 5.67e10], + "financial": { + "revenue": 1000000.50, + "expenses": 750000.75, + "profit_margin": 0.2499 + }, + "measurements": { + "temperature": -40.5, + "distance": 1000.25, + "weight": 0.5 + } + }` + + freeze.SnapJSON(t, "SnapJSON With Numbers", jsonStr) +} + +// TestSnapJSONWithSpecialCharacters tests SnapJSON with special chars +func TestSnapJSONWithSpecialCharacters(t *testing.T) { + jsonStr := `{ + "special": "!@#$%^&*()_+-=[]{}|;:',.<>?/", + "escaped": "line1\nline2\ttab\rcarriage", + "quotes": "He said \"hello\" and she said 'goodbye'", + "unicode": "Hello δΈ–η•Œ 🌍 Ω…Ψ±Ψ­Ψ¨Ψ§ ΠŸΡ€ΠΈΠ²Π΅Ρ‚", + "paths": "C:\\Users\\name\\Documents\\file.txt", + "html": "
Content
", + "regex": "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$" + }` + + freeze.SnapJSON(t, "SnapJSON With Special Characters", jsonStr) +} + +// TestSnapJSONEmptyStructures tests SnapJSON with empty collections +func TestSnapJSONEmptyStructures(t *testing.T) { + jsonStr := `{ + "empty_array": [], + "empty_object": {}, + "empty_string": "", + "zero": 0, + "false_value": false, + "null_value": null, + "nested": { + "empty": [], + "also_empty": {} + } + }` + + freeze.SnapJSON(t, "SnapJSON Empty Structures", jsonStr) +} + +// TestSnapJSONMixedTypes tests SnapJSON with mixed array types +func TestSnapJSONMixedTypes(t *testing.T) { + jsonStr := `{ + "mixed_array": [ + "string", + 123, + 45.67, + true, + false, + null, + {"nested": "object"}, + [1, 2, 3] + ], + "complex": [ + {"type": "user", "id": 1}, + {"type": "post", "id": 100}, + [1, 2, 3], + "string", + null + ] + }` + + freeze.SnapJSON(t, "SnapJSON Mixed Types", jsonStr) +} + +// TestSnapJSONRealWorldExample tests SnapJSON with real-world API data +func TestSnapJSONRealWorldExample(t *testing.T) { + jsonStr := `{ + "success": true, + "data": { + "product": { + "id": "prod_12345", + "name": "Premium Wireless Headphones", + "sku": "PWH-001", + "description": "High-quality wireless headphones with noise cancellation", + "price": { + "amount": 199.99, + "currency": "USD", + "discount": 10, + "final_price": 179.99 + }, + "inventory": { + "total": 500, + "available": 425, + "reserved": 50, + "damaged": 25 + }, + "specifications": { + "battery_life": "30 hours", + "weight": "250g", + "colors": ["black", "white", "blue"], + "warranty_months": 24 + }, + "ratings": { + "average": 4.5, + "count": 1250, + "breakdown": { + "5": 750, + "4": 350, + "3": 100, + "2": 30, + "1": 20 + } + }, + "reviews": [ + { + "id": "rev_001", + "user": "john_doe", + "rating": 5, + "title": "Excellent product!", + "content": "Great sound quality and comfortable to wear.", + "helpful": 25, + "created_at": "2023-11-15T10:30:00Z" + }, + { + "id": "rev_002", + "user": "jane_smith", + "rating": 4, + "title": "Good but pricey", + "content": "Works well, could be cheaper.", + "helpful": 12, + "created_at": "2023-11-10T14:20:00Z" + } + ] + }, + "related_products": [ + { + "id": "prod_12346", + "name": "Headphone Case", + "price": 29.99 + }, + { + "id": "prod_12347", + "name": "Audio Cable", + "price": 14.99 + } + ] + }, + "request_id": "req_abc123def456", + "timestamp": "2023-11-18T22:00:00Z" + }` + + freeze.SnapJSON(t, "SnapJSON Real World Example", jsonStr) +} + +// ============================================================================ // HELPER FUNCTIONS // ============================================================================ diff --git a/__snapshots__/test_complex_j_s_o_n_structure.snap b/__snapshots__/test_complex_j_s_o_n_structure.snap --- a/__snapshots__/test_complex_j_s_o_n_structure.snap +++ b/__snapshots__/test_complex_j_s_o_n_structure.snap @@ -7,9 +7,11 @@ --- map[string]interface{}{ "api": map[string]interface{}{ - "version": "2.0", "endpoints": []interface{}{ map[string]interface{}{ + "auth_required": true, + "method": "GET", + "path": "/users", "rate_limit": map[string]interface{}{ "requests": 100.0, "window": "1m", @@ -18,9 +20,7 @@ "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", @@ -29,49 +29,48 @@ "type": "string", }, }, + "type": "object", }, + "type": "array", }, }, "401": map[string]interface{}{ "description": "Unauthorized", }, }, - "path": "/users", - "method": "GET", - "auth_required": true, }, map[string]interface{}{ "auth_required": true, + "method": "POST", + "path": "/users/{id}", "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", + "type": "string", }, - "roles": map[string]interface{}{ - "type": "array", - "items": map[string]interface{}{ - "type": "string", - }, + "email": map[string]interface{}{ + "format": "email", + "type": "string", }, "id": map[string]interface{}{ "type": "integer", }, + "roles": map[string]interface{}{ + "items": map[string]interface{}{ + "type": "string", + }, + "type": "array", + }, "username": map[string]interface{}{ "type": "string", - }, - "email": map[string]interface{}{ - "type": "string", - "format": "email", }, }, "required": []interface{}{ @@ -81,5 +80,6 @@ }, }, }, + "version": "2.0", }, } diff --git a/__snapshots__/test_complex_nested_structure.snap b/__snapshots__/test_complex_nested_structure.snap --- a/__snapshots__/test_complex_nested_structure.snap +++ b/__snapshots__/test_complex_nested_structure.snap @@ -26,12 +26,12 @@ }, Metadata: map[string]interface{}{ "language": "en", + "notifications": true, "preferences": map[string]interface{}{ "email_frequency": "weekly", "notifications": true, }, "theme": "dark", - "notifications": true, }, }, Tags: []string{ 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 --- a/__snapshots__/test_go_struct_marshalled_to_j_s_o_n.snap +++ b/__snapshots__/test_go_struct_marshalled_to_j_s_o_n.snap @@ -6,19 +6,19 @@ version: 0.1.0 --- map[string]interface{}{ - "name": "Jane Smith", - "email": "jane@example.com", - "phone": "+1-555-0123", + "active": true, "address": map[string]interface{}{ - "street": "456 Oak Ave", "city": "San Francisco", + "street": "456 Oak Ave", "zip": "94102", }, + "created_at": "2023-06-15T14:30:00Z", + "email": "jane@example.com", + "name": "Jane Smith", + "phone": "+1-555-0123", "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 --- a/__snapshots__/test_j_s_o_n_array_of_objects.snap +++ b/__snapshots__/test_j_s_o_n_array_of_objects.snap @@ -7,29 +7,29 @@ --- []interface{}{ map[string]interface{}{ - "type": "user", - "id": 1.0, "data": map[string]interface{}{ - "role": "admin", "name": "Alice", + "role": "admin", }, + "id": 1.0, + "type": "user", }, map[string]interface{}{ - "type": "post", - "id": 100.0, "data": map[string]interface{}{ + "author_id": 1.0, "likes": 42.0, "title": "First Post", - "author_id": 1.0, }, + "id": 100.0, + "type": "post", }, map[string]interface{}{ - "type": "comment", - "id": 500.0, "data": map[string]interface{}{ - "post_id": 100.0, - "content": "Great post!", "author_id": 2.0, + "content": "Great post!", + "post_id": 100.0, }, + "id": 500.0, + "type": "comment", }, } diff --git a/__snapshots__/test_j_s_o_n_numbers.snap b/__snapshots__/test_j_s_o_n_numbers.snap --- a/__snapshots__/test_j_s_o_n_numbers.snap +++ b/__snapshots__/test_j_s_o_n_numbers.snap @@ -6,20 +6,20 @@ version: 0.1.0 --- map[string]interface{}{ + "edge_cases": map[string]interface{}{ + "minus_one": -1.0, + "one": 1.0, + }, "floats": map[string]interface{}{ + "negative_float": -42.5, "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, + "negative": -100.0, + "positive": 42.0, + "zero": 0.0, }, } diff --git a/__snapshots__/test_j_s_o_n_object.snap b/__snapshots__/test_j_s_o_n_object.snap --- a/__snapshots__/test_j_s_o_n_object.snap +++ b/__snapshots__/test_j_s_o_n_object.snap @@ -6,22 +6,22 @@ version: 0.1.0 --- map[string]interface{}{ + "message": nil, + "status": "success", "user": map[string]interface{}{ + "created_at": "2023-01-15T10:30:00Z", + "email": "john@example.com", + "id": 1.0, + "profile": map[string]interface{}{ + "bio": "Software engineer", + "first_name": "John", + "last_name": "Doe", + "verified": true, + }, "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 --- a/__snapshots__/test_j_s_o_n_with_mixed_arrays.snap +++ b/__snapshots__/test_j_s_o_n_with_mixed_arrays.snap @@ -44,12 +44,12 @@ "name": "Item 1", }, map[string]interface{}{ - "name": "Item 2", "id": 2.0, + "name": "Item 2", }, map[string]interface{}{ - "name": "Item 3", "id": 3.0, + "name": "Item 3", }, }, } diff --git a/__snapshots__/test_j_s_o_n_with_special_characters.snap b/__snapshots__/test_j_s_o_n_with_special_characters.snap --- a/__snapshots__/test_j_s_o_n_with_special_characters.snap +++ b/__snapshots__/test_j_s_o_n_with_special_characters.snap @@ -6,12 +6,12 @@ version: 0.1.0 --- map[string]interface{}{ - "tabs": "col1\tcol2\tcol3", "backslash": "path\\to\\file", - "english": "Hello, World!", - "unicode": "こんにけは δΈ–η•Œ 🌍", "emoji": "πŸ˜€ πŸ˜ƒ πŸ˜„ 😁 πŸ˜†", - "special_chars": "!@#$%^&*()_+-=[]{}|;:,.<>?", + "english": "Hello, World!", "escaped": "quotes: \"double\" and 'single'", "newlines": "line1\nline2\rline3\r\nline4", + "special_chars": "!@#$%^&*()_+-=[]{}|;:,.<>?", + "tabs": "col1\tcol2\tcol3", + "unicode": "こんにけは δΈ–η•Œ 🌍", } diff --git a/__snapshots__/test_j_s_o_n_with_various_types.snap b/__snapshots__/test_j_s_o_n_with_various_types.snap --- a/__snapshots__/test_j_s_o_n_with_various_types.snap +++ b/__snapshots__/test_j_s_o_n_with_various_types.snap @@ -6,9 +6,6 @@ version: 0.1.0 --- map[string]interface{}{ - "float": 3.14159, - "boolean_true": true, - "null_value": nil, "array": []interface{}{ 1.0, 2.0, @@ -16,16 +13,19 @@ "four", 5.5, }, + "boolean_false": false, + "boolean_true": true, "empty_array": []interface{}{ }, "empty_object": map[string]interface{}{ }, "escaped_string": "line1\nline2\ttab", - "string": "hello world", + "float": 3.14159, "integer": 42.0, - "boolean_false": false, + "null_value": nil, "object": map[string]interface{}{ - "nested": "value", "count": 10.0, + "nested": "value", }, + "string": "hello world", } diff --git a/__snapshots__/test_large_j_s_o_n.snap b/__snapshots__/test_large_j_s_o_n.snap --- a/__snapshots__/test_large_j_s_o_n.snap +++ b/__snapshots__/test_large_j_s_o_n.snap @@ -6,50 +6,50 @@ 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", + "customer_id": 42.0, "delivered_at": nil, "id": 1001.0, - "customer_id": 42.0, "products": []interface{}{ map[string]interface{}{ + "description": "High-performance laptop", + "id": 1.0, + "in_stock": true, + "name": "Laptop", "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", + "id": 2.0, + "in_stock": true, + "name": "Mouse", "price": 29.99, "stock": 50.0, - "in_stock": true, "tags": []interface{}{ "electronics", "accessories", }, - "id": 2.0, }, map[string]interface{}{ "description": "Mechanical keyboard", + "id": 3.0, + "in_stock": false, + "name": "Keyboard", "price": 149.99, "stock": 0.0, - "in_stock": false, "tags": []interface{}{ "electronics", "accessories", }, - "id": 3.0, - "name": "Keyboard", }, }, + "shipped_at": "2023-02-01T10:00:00Z", + "status": "shipped", + "total": 1179.97, } diff --git a/__snapshots__/test_multiple_complex_structures.snap b/__snapshots__/test_multiple_complex_structures.snap --- a/__snapshots__/test_multiple_complex_structures.snap +++ b/__snapshots__/test_multiple_complex_structures.snap @@ -21,8 +21,8 @@ "moderator", }, Metadata: map[string]interface{}{ - "verified": true, "badge": "verified", + "verified": true, }, }, { @@ -39,8 +39,8 @@ "user", }, Metadata: map[string]interface{}{ - "verified": false, "avatar": "https://example.com/bob.jpg", + "verified": false, }, }, { @@ -58,8 +58,8 @@ "admin", }, Metadata: map[string]interface{}{ - "verified": true, "account_age_days": 365, + "verified": true, }, }, } diff --git a/__snapshots__/test_nested_maps_and_slices.snap b/__snapshots__/test_nested_maps_and_slices.snap --- a/__snapshots__/test_nested_maps_and_slices.snap +++ b/__snapshots__/test_nested_maps_and_slices.snap @@ -6,6 +6,29 @@ version: 0.1.0 --- map[string]interface{}{ + "posts": map[string]interface{}{ + "categories": []string{ + "tech", + "lifestyle", + "news", + }, + "drafts": 5, + "published": 42, + }, + "stats": map[string]interface{}{ + "daily": map[string]interface{}{ + "clicks": 320, + "conversions": map[string]interface{}{ + "by_source": map[string]int{ + "organic": 25, + "paid": 15, + "referral": 5, + }, + "total": 45, + }, + "views": 1500, + }, + }, "users": map[string]interface{}{ "active": []map[string]interface{}{ { @@ -23,29 +46,6 @@ { "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_snap_j_s_o_n_array_of_objects.snap b/__snapshots__/test_snap_j_s_o_n_array_of_objects.snap new file mode 100644 --- /dev/null +++ b/__snapshots__/test_snap_j_s_o_n_array_of_objects.snap @@ -0,0 +1,30 @@ +--- +title: SnapJSON Array of Objects +test_name: TestSnapJSONArrayOfObjects +file_path: +func_name: +version: 0.1.0 +--- +[]interface{}{ + map[string]interface{}{ + "id": 1.0, + "likes": 42.0, + "title": "First Post", + "type": "post", + "views": 150.0, + }, + map[string]interface{}{ + "id": 2.0, + "likes": 75.0, + "title": "Second Post", + "type": "post", + "views": 280.0, + }, + map[string]interface{}{ + "id": 3.0, + "likes": 120.0, + "title": "Third Post", + "type": "post", + "views": 450.0, + }, +} diff --git a/__snapshots__/test_snap_j_s_o_n_basic.snap b/__snapshots__/test_snap_j_s_o_n_basic.snap new file mode 100644 --- /dev/null +++ b/__snapshots__/test_snap_j_s_o_n_basic.snap @@ -0,0 +1,13 @@ +--- +title: SnapJSON Basic Object +test_name: TestSnapJSONBasic +file_path: +func_name: +version: 0.1.0 +--- +map[string]interface{}{ + "age": 30.0, + "email": "john@example.com", + "name": "John Doe", + "verified": true, +} diff --git a/__snapshots__/test_snap_j_s_o_n_compact_format.snap b/__snapshots__/test_snap_j_s_o_n_compact_format.snap new file mode 100644 --- /dev/null +++ b/__snapshots__/test_snap_j_s_o_n_compact_format.snap @@ -0,0 +1,17 @@ +--- +title: SnapJSON Compact Format +test_name: TestSnapJSONCompactFormat +file_path: +func_name: +version: 0.1.0 +--- +map[string]interface{}{ + "id": 1.0, + "in_stock": true, + "name": "Product", + "price": 99.99, + "tags": []interface{}{ + "electronics", + "gadgets", + }, +} diff --git a/__snapshots__/test_snap_j_s_o_n_complex_a_p_i.snap b/__snapshots__/test_snap_j_s_o_n_complex_a_p_i.snap new file mode 100644 --- /dev/null +++ b/__snapshots__/test_snap_j_s_o_n_complex_a_p_i.snap @@ -0,0 +1,43 @@ +--- +title: SnapJSON Complex API Response +test_name: TestSnapJSONComplexAPI +file_path: +func_name: +version: 0.1.0 +--- +map[string]interface{}{ + "code": 200.0, + "data": map[string]interface{}{ + "pagination": map[string]interface{}{ + "page": 1.0, + "per_page": 10.0, + "total": 3.0, + "total_pages": 1.0, + }, + "users": []interface{}{ + map[string]interface{}{ + "active": true, + "department": "Engineering", + "id": 1.0, + "name": "Alice", + "role": "admin", + }, + map[string]interface{}{ + "active": true, + "department": "Sales", + "id": 2.0, + "name": "Bob", + "role": "user", + }, + map[string]interface{}{ + "active": false, + "department": "Marketing", + "id": 3.0, + "name": "Charlie", + "role": "user", + }, + }, + }, + "status": "success", + "timestamp": "2023-11-18T21:45:30Z", +} diff --git a/__snapshots__/test_snap_j_s_o_n_empty_structures.snap b/__snapshots__/test_snap_j_s_o_n_empty_structures.snap new file mode 100644 --- /dev/null +++ b/__snapshots__/test_snap_j_s_o_n_empty_structures.snap @@ -0,0 +1,23 @@ +--- +title: SnapJSON Empty Structures +test_name: TestSnapJSONEmptyStructures +file_path: +func_name: +version: 0.1.0 +--- +map[string]interface{}{ + "empty_array": []interface{}{ + }, + "empty_object": map[string]interface{}{ + }, + "empty_string": "", + "false_value": false, + "nested": map[string]interface{}{ + "also_empty": map[string]interface{}{ + }, + "empty": []interface{}{ + }, + }, + "null_value": nil, + "zero": 0.0, +} diff --git a/__snapshots__/test_snap_j_s_o_n_large_nested_structure.snap b/__snapshots__/test_snap_j_s_o_n_large_nested_structure.snap new file mode 100644 --- /dev/null +++ b/__snapshots__/test_snap_j_s_o_n_large_nested_structure.snap @@ -0,0 +1,105 @@ +--- +title: SnapJSON Large Nested Structure +test_name: TestSnapJSONLargeNestedStructure +file_path: +func_name: +version: 0.1.0 +--- +map[string]interface{}{ + "organization": map[string]interface{}{ + "departments": []interface{}{ + map[string]interface{}{ + "manager": "Alice", + "name": "Engineering", + "teams": []interface{}{ + map[string]interface{}{ + "lead": "John", + "members": []interface{}{ + map[string]interface{}{ + "id": 1.0, + "level": "senior", + "name": "John", + }, + map[string]interface{}{ + "id": 2.0, + "level": "mid", + "name": "Jane", + }, + }, + "name": "Backend", + "projects": []interface{}{ + map[string]interface{}{ + "id": "proj_1", + "name": "API Service", + "status": "active", + }, + map[string]interface{}{ + "id": "proj_2", + "name": "Database Optimization", + "status": "planning", + }, + }, + }, + map[string]interface{}{ + "lead": "Bob", + "members": []interface{}{ + map[string]interface{}{ + "id": 3.0, + "level": "senior", + "name": "Bob", + }, + map[string]interface{}{ + "id": 4.0, + "level": "junior", + "name": "Carol", + }, + }, + "name": "Frontend", + "projects": []interface{}{ + map[string]interface{}{ + "id": "proj_3", + "name": "Web App", + "status": "active", + }, + }, + }, + }, + }, + map[string]interface{}{ + "manager": "Charlie", + "name": "Sales", + "teams": []interface{}{ + map[string]interface{}{ + "lead": "Dave", + "members": []interface{}{ + map[string]interface{}{ + "id": 5.0, + "level": "senior", + "name": "Dave", + }, + map[string]interface{}{ + "id": 6.0, + "level": "mid", + "name": "Eve", + }, + }, + "name": "Enterprise", + "projects": []interface{}{ + }, + }, + }, + }, + }, + "id": "org_123", + "metadata": map[string]interface{}{ + "employees": 150.0, + "founded": "2020", + "locations": []interface{}{ + "USA", + "EU", + "APAC", + }, + }, + "name": "TechCorp", + }, +} diff --git a/__snapshots__/test_snap_j_s_o_n_mixed_types.snap b/__snapshots__/test_snap_j_s_o_n_mixed_types.snap new file mode 100644 --- /dev/null +++ b/__snapshots__/test_snap_j_s_o_n_mixed_types.snap @@ -0,0 +1,42 @@ +--- +title: SnapJSON Mixed Types +test_name: TestSnapJSONMixedTypes +file_path: +func_name: +version: 0.1.0 +--- +map[string]interface{}{ + "complex": []interface{}{ + map[string]interface{}{ + "id": 1.0, + "type": "user", + }, + map[string]interface{}{ + "id": 100.0, + "type": "post", + }, + []interface{}{ + 1.0, + 2.0, + 3.0, + }, + "string", + nil, + }, + "mixed_array": []interface{}{ + "string", + 123.0, + 45.67, + true, + false, + nil, + map[string]interface{}{ + "nested": "object", + }, + []interface{}{ + 1.0, + 2.0, + 3.0, + }, + }, +} diff --git a/__snapshots__/test_snap_j_s_o_n_real_world_example.snap b/__snapshots__/test_snap_j_s_o_n_real_world_example.snap new file mode 100644 --- /dev/null +++ b/__snapshots__/test_snap_j_s_o_n_real_world_example.snap @@ -0,0 +1,85 @@ +--- +title: SnapJSON Real World Example +test_name: TestSnapJSONRealWorldExample +file_path: +func_name: +version: 0.1.0 +--- +map[string]interface{}{ + "data": map[string]interface{}{ + "product": map[string]interface{}{ + "description": "High-quality wireless headphones with noise cancellation", + "id": "prod_12345", + "inventory": map[string]interface{}{ + "available": 425.0, + "damaged": 25.0, + "reserved": 50.0, + "total": 500.0, + }, + "name": "Premium Wireless Headphones", + "price": map[string]interface{}{ + "amount": 199.99, + "currency": "USD", + "discount": 10.0, + "final_price": 179.99, + }, + "ratings": map[string]interface{}{ + "average": 4.5, + "breakdown": map[string]interface{}{ + "1": 20.0, + "2": 30.0, + "3": 100.0, + "4": 350.0, + "5": 750.0, + }, + "count": 1250.0, + }, + "reviews": []interface{}{ + map[string]interface{}{ + "content": "Great sound quality and comfortable to wear.", + "created_at": "2023-11-15T10:30:00Z", + "helpful": 25.0, + "id": "rev_001", + "rating": 5.0, + "title": "Excellent product!", + "user": "john_doe", + }, + map[string]interface{}{ + "content": "Works well, could be cheaper.", + "created_at": "2023-11-10T14:20:00Z", + "helpful": 12.0, + "id": "rev_002", + "rating": 4.0, + "title": "Good but pricey", + "user": "jane_smith", + }, + }, + "sku": "PWH-001", + "specifications": map[string]interface{}{ + "battery_life": "30 hours", + "colors": []interface{}{ + "black", + "white", + "blue", + }, + "warranty_months": 24.0, + "weight": "250g", + }, + }, + "related_products": []interface{}{ + map[string]interface{}{ + "id": "prod_12346", + "name": "Headphone Case", + "price": 29.99, + }, + map[string]interface{}{ + "id": "prod_12347", + "name": "Audio Cable", + "price": 14.99, + }, + }, + }, + "request_id": "req_abc123def456", + "success": true, + "timestamp": "2023-11-18T22:00:00Z", +} diff --git a/__snapshots__/test_snap_j_s_o_n_simple_array.snap b/__snapshots__/test_snap_j_s_o_n_simple_array.snap new file mode 100644 --- /dev/null +++ b/__snapshots__/test_snap_j_s_o_n_simple_array.snap @@ -0,0 +1,13 @@ +--- +title: SnapJSON Simple Array +test_name: TestSnapJSONSimpleArray +file_path: +func_name: +version: 0.1.0 +--- +[]interface{}{ + "apple", + "banana", + "orange", + "grape", +} diff --git a/__snapshots__/test_snap_j_s_o_n_with_nested_objects.snap b/__snapshots__/test_snap_j_s_o_n_with_nested_objects.snap new file mode 100644 --- /dev/null +++ b/__snapshots__/test_snap_j_s_o_n_with_nested_objects.snap @@ -0,0 +1,27 @@ +--- +title: SnapJSON Nested Objects +test_name: TestSnapJSONWithNestedObjects +file_path: +func_name: +version: 0.1.0 +--- +map[string]interface{}{ + "created_at": "2023-06-15T10:30:00Z", + "user": map[string]interface{}{ + "id": 42.0, + "permissions": []interface{}{ + "read", + "write", + "admin", + }, + "profile": map[string]interface{}{ + "avatar": "https://example.com/avatar.jpg", + "settings": map[string]interface{}{ + "language": "en", + "notifications": true, + "theme": "dark", + }, + "username": "jane_smith", + }, + }, +} diff --git a/__snapshots__/test_snap_j_s_o_n_with_nulls.snap b/__snapshots__/test_snap_j_s_o_n_with_nulls.snap new file mode 100644 --- /dev/null +++ b/__snapshots__/test_snap_j_s_o_n_with_nulls.snap @@ -0,0 +1,19 @@ +--- +title: SnapJSON With Nulls +test_name: TestSnapJSONWithNulls +file_path: +func_name: +version: 0.1.0 +--- +map[string]interface{}{ + "category": nil, + "description": nil, + "id": 1.0, + "metadata": map[string]interface{}{ + "created": "2023-01-01", + "deleted": nil, + "updated": nil, + }, + "name": "Item", + "tags": nil, +} diff --git a/__snapshots__/test_snap_j_s_o_n_with_numbers.snap b/__snapshots__/test_snap_j_s_o_n_with_numbers.snap new file mode 100644 --- /dev/null +++ b/__snapshots__/test_snap_j_s_o_n_with_numbers.snap @@ -0,0 +1,35 @@ +--- +title: SnapJSON With Numbers +test_name: TestSnapJSONWithNumbers +file_path: +func_name: +version: 0.1.0 +--- +map[string]interface{}{ + "financial": map[string]interface{}{ + "expenses": 750000.75, + "profit_margin": 0.2499, + "revenue": 1.0000005e+06, + }, + "floats": []interface{}{ + 0.0, + 3.14, + -2.5, + 0.001, + 0.000123, + 5.67e+10.0, + }, + "integers": []interface{}{ + 0.0, + 1.0, + -1.0, + 42.0, + -100.0, + 9.999999e+06.0, + }, + "measurements": map[string]interface{}{ + "distance": 1000.25, + "temperature": -40.5, + "weight": 0.5, + }, +} diff --git a/__snapshots__/test_snap_j_s_o_n_with_special_characters.snap b/__snapshots__/test_snap_j_s_o_n_with_special_characters.snap new file mode 100644 --- /dev/null +++ b/__snapshots__/test_snap_j_s_o_n_with_special_characters.snap @@ -0,0 +1,16 @@ +--- +title: SnapJSON With Special Characters +test_name: TestSnapJSONWithSpecialCharacters +file_path: +func_name: +version: 0.1.0 +--- +map[string]interface{}{ + "escaped": "line1\nline2\ttab\rcarriage", + "html": "
Content
", + "paths": "C:\\Users\\name\\Documents\\file.txt", + "quotes": "He said \"hello\" and she said 'goodbye'", + "regex": "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$", + "special": "!@#$%^&*()_+-=[]{}|;:',.<>?/", + "unicode": "Hello δΈ–η•Œ 🌍 Ω…Ψ±Ψ­Ψ¨Ψ§ ΠŸΡ€ΠΈΠ²Π΅Ρ‚", +} diff --git a/__snapshots__/test_structure_with_empty_values.snap b/__snapshots__/test_structure_with_empty_values.snap --- a/__snapshots__/test_structure_with_empty_values.snap +++ b/__snapshots__/test_structure_with_empty_values.snap @@ -29,8 +29,8 @@ "c", }, Tags: map[string]string{ - "type": "test", "env": "dev", + "type": "test", }, OptionalID: &int(42), Count: 3, diff --git a/__snapshots__/test_structure_with_interface.snap b/__snapshots__/test_structure_with_interface.snap --- a/__snapshots__/test_structure_with_interface.snap +++ b/__snapshots__/test_structure_with_interface.snap @@ -69,9 +69,9 @@ }, }, Meta: map[string]interface{}{ - "total_count": 10, "page": 1, "per_page": 20, + "total_count": 10, }, }, } -- tangled.sh