diff --git a/go.mod b/go.mod index 1f961e26..7beebd60 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ toolchain go1.24.2 require ( github.com/codecrafters-io/tester-utils v0.4.9 + github.com/dustin/go-humanize v1.0.1 github.com/hdt3213/rdb v1.2.0 github.com/stretchr/testify v1.10.0 github.com/tidwall/pretty v1.2.1 @@ -14,7 +15,6 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dustin/go-humanize v1.0.1 // indirect github.com/fatih/color v1.18.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect diff --git a/internal/resp_assertions/bulk_string_assertion.go b/internal/resp_assertions/bulk_string_assertion.go index c5d3e21c..a15a1301 100644 --- a/internal/resp_assertions/bulk_string_assertion.go +++ b/internal/resp_assertions/bulk_string_assertion.go @@ -15,6 +15,15 @@ func NewBulkStringAssertion(expectedValue string) RESPAssertion { } func (a BulkStringAssertion) Run(value resp_value.Value) error { + // Frequently occurs in user submissions + if value.Type == resp_value.SIMPLE_STRING && value.String() == a.ExpectedValue { + return fmt.Errorf( + "Expected bulk string \"%s\", got simple string \"%s\" instead", + a.ExpectedValue, + value.String(), + ) + } + bulkStringTypeAssertion := DataTypeAssertion{ExpectedType: resp_value.BULK_STRING} if err := bulkStringTypeAssertion.Run(value); err != nil { diff --git a/internal/resp_assertions/bulk_string_present_in_array_assertion.go b/internal/resp_assertions/bulk_string_present_in_array_assertion.go index 05a0103a..03017f6e 100644 --- a/internal/resp_assertions/bulk_string_present_in_array_assertion.go +++ b/internal/resp_assertions/bulk_string_present_in_array_assertion.go @@ -25,5 +25,16 @@ func (a BulkStringPresentInArrayAssertion) Run(value resp_value.Value) error { } } + // Possible frequent-occurence inferring from the existing errors in user submissions + for _, element := range array { + if element.Type == resp_value.SIMPLE_STRING && element.String() == a.ExpectedString { + return fmt.Errorf( + "Expected bulk string '%s' to be present in the array, but simple string '%s' is present instead", + a.ExpectedString, + element.String(), + ) + } + } + return fmt.Errorf("Expected bulk string '%s' to be present in the array, but is absent", a.ExpectedString) } diff --git a/internal/resp_assertions/simple_string_assertion.go b/internal/resp_assertions/simple_string_assertion.go index f0febec8..16b40e97 100644 --- a/internal/resp_assertions/simple_string_assertion.go +++ b/internal/resp_assertions/simple_string_assertion.go @@ -15,6 +15,15 @@ func NewSimpleStringAssertion(expectedValue string) RESPAssertion { } func (a SimpleStringAssertion) Run(value resp_value.Value) error { + // Frequently occurs in user submissions + if value.Type == resp_value.BULK_STRING && value.String() == a.ExpectedValue { + return fmt.Errorf( + "Expected simple string \"%s\", got bulk string \"%s\" instead", + a.ExpectedValue, + value.String(), + ) + } + simpleStringTypeAssertion := DataTypeAssertion{ExpectedType: resp_value.SIMPLE_STRING} if err := simpleStringTypeAssertion.Run(value); err != nil { diff --git a/internal/stages_test.go b/internal/stages_test.go index 55545856..cfd72857 100644 --- a/internal/stages_test.go +++ b/internal/stages_test.go @@ -61,6 +61,13 @@ func TestStages(t *testing.T) { StdoutFixturePath: "./test_helpers/fixtures/ping-pong/without_read_multiple_pongs", NormalizeOutputFunc: normalizeTesterOutput, }, + "ping_pong_string_type_mismatch": { + StageSlugs: []string{"rg2"}, + CodePath: "./test_helpers/scenarios/ping-pong/string_type_mismatch", + ExpectedExitCode: 1, + StdoutFixturePath: "./test_helpers/fixtures/ping-pong/string_type_mismatch", + NormalizeOutputFunc: normalizeTesterOutput, + }, "invalid_resp_error": { StageSlugs: []string{"rg2"}, CodePath: "./test_helpers/scenarios/invalid-resp/", diff --git a/internal/test_helpers/fixtures/ping-pong/string_type_mismatch b/internal/test_helpers/fixtures/ping-pong/string_type_mismatch new file mode 100644 index 00000000..d07f08ac --- /dev/null +++ b/internal/test_helpers/fixtures/ping-pong/string_type_mismatch @@ -0,0 +1,14 @@ +Debug = true + +[tester::#RG2] Running tests for Stage #RG2 (rg2) +[tester::#RG2] $ ./spawn_redis_server.sh +[your_program] hey, binding to 6379 +[tester::#RG2] Connection established, sending ping command... +[tester::#RG2] [client] $ redis-cli PING +[tester::#RG2] [client] Sent bytes: "*1\r\n$4\r\nPING\r\n" +[tester::#RG2] [client] Received bytes: "$4\r\nPONG\r\n" +[tester::#RG2] [client] Received RESP bulk string: "PONG" +[tester::#RG2] Expected simple string "PONG", got bulk string "PONG" instead +[tester::#RG2] Test failed +[tester::#RG2] Terminating program +[tester::#RG2] Program terminated successfully diff --git a/internal/test_helpers/scenarios/ping-pong/string_type_mismatch/codecrafters.yml b/internal/test_helpers/scenarios/ping-pong/string_type_mismatch/codecrafters.yml new file mode 100644 index 00000000..f544b940 --- /dev/null +++ b/internal/test_helpers/scenarios/ping-pong/string_type_mismatch/codecrafters.yml @@ -0,0 +1,5 @@ +# Set this to true if you want debug logs. +# +# These can be VERY verbose, so we suggest turning them off +# unless you really need them. +debug: true \ No newline at end of file diff --git a/internal/test_helpers/scenarios/ping-pong/string_type_mismatch/spawn_redis_server.sh b/internal/test_helpers/scenarios/ping-pong/string_type_mismatch/spawn_redis_server.sh new file mode 100755 index 00000000..a9647891 --- /dev/null +++ b/internal/test_helpers/scenarios/ping-pong/string_type_mismatch/spawn_redis_server.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env -S python3 -u +import socket +import time +print("hey, binding to 6379") +sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + +sock.bind(('', 6379)) + +sock.listen(1) + +conn, cli_addr = sock.accept() + +# Send bulk string instead of simple string +conn.send(b"$4\r\nPONG\r\n")