From 9e66a1aad5025696e90ac3ab2a543c1720cc2b9d Mon Sep 17 00:00:00 2001 From: TakshakRamteke Date: Sun, 26 Jul 2026 23:07:28 +0530 Subject: [PATCH 1/6] fix: added some unit tests for vm --- internals/repl/repl_test.go | 8 ++++++ internals/vm/vm_test.go | 50 +++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 internals/repl/repl_test.go create mode 100644 internals/vm/vm_test.go diff --git a/internals/repl/repl_test.go b/internals/repl/repl_test.go new file mode 100644 index 0000000..23b26ec --- /dev/null +++ b/internals/repl/repl_test.go @@ -0,0 +1,8 @@ +package repl + +// TODO + +// in trying to implement unit test for Run we would kinda endup implementing a test which would be somewhat similar to +// e2e test which we already have in place under ../tests directory +// PrintConsole simply prints godb > to the console, so not sure if we would need a test for that +// Skipping unit test for this, while marking it as a TODO diff --git a/internals/vm/vm_test.go b/internals/vm/vm_test.go new file mode 100644 index 0000000..e1701ed --- /dev/null +++ b/internals/vm/vm_test.go @@ -0,0 +1,50 @@ +package vm + +import ( + "godb/internals/buffer" + "testing" +) + +var inputBuffer = buffer.NewInputBuffer() + +var statement = NewStatement() + +func comapareExpectedAndRetruned(t *testing.T, result, expectedResult PrepareResults, resultingStype, expectedStype statementType) { + if result != expectedResult && resultingStype == expectedStype { + t.Errorf("Expected prepareResult and stype to be: (%v, %v), got: (%v,%v)\n", expectedResult, expectedStype, result, resultingStype) + } +} + +func TestPrepareStatementSelect(t *testing.T) { + inputBuffer.Buffer = "select" + + result := statement.PrepareStatements(inputBuffer) + + comapareExpectedAndRetruned(t, result, PrepareSuccess, statement.stype, StatementSelect) +} + +func TestPrepareStatementInsert(t *testing.T) { + inputBuffer.Buffer = "insert 0 hello hello.com" + + result := statement.PrepareStatements(inputBuffer) + + comapareExpectedAndRetruned(t, result, PrepareSuccess, statement.stype, StatementInsert) +} + +func TestPrepareStatementUnrecognized(t *testing.T) { + inputBuffer.Buffer = "something" + + result := statement.PrepareStatements(inputBuffer) + + comapareExpectedAndRetruned(t, result, PrepareUnrecognised, statement.stype, StatementUnrecognized) +} + +func TestPrepareStatementSyntaxError(t *testing.T) { + inputBuffer.Buffer = "insert 0 hello" + + result := statement.PrepareStatements(inputBuffer) + + if result != PrepareSyntaxError { + t.Errorf("Expected prepareResult to be: %v, got: %v\n", PrepareSyntaxError, result) + } +} -- 2.54.0 From 1e297a45abf03d202e2f4436af83528b3364e042 Mon Sep 17 00:00:00 2001 From: TakshakRamteke Date: Tue, 28 Jul 2026 00:40:10 +0530 Subject: [PATCH 2/6] feat: added a test for table max capacity --- tests/main_test.go | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/tests/main_test.go b/tests/main_test.go index 6d4551d..029e0b7 100644 --- a/tests/main_test.go +++ b/tests/main_test.go @@ -35,15 +35,21 @@ func runner(commands []string, t *testing.T) []string { go func() { defer stdin.Close() for _, c := range commands { - _, _ = io.WriteString(stdin, c+"\n") + _, err = io.WriteString(stdin, c+"\n") + if err != nil { + t.Errorf("Failed to execute %v because of %q\n", c, err) + break + } } }() reader := bufio.NewReader(stdout) for { + // fmt.Printf("reading command outputs...\n") line, err := reader.ReadString('\n') if line != "" { formattedline := fmt.Sprintf("%v", strings.Trim(strings.ReplaceAll(line, "\x00", ""), "\n")) + // fmt.Printf("formatting and appending result: %v\n", formattedline) results = append(results, formattedline) } if err != nil { @@ -83,3 +89,20 @@ func TestMain(t *testing.T) { t.Errorf("Output Mismatch\nExpected: %v\nGot: %v\n", expected, results) } } + +func TestTableFull(t *testing.T) { + maxRows := 1400 + commands := make([]string, 0) + + for i := range maxRows { + c := fmt.Sprintf("insert %d user%d person%d@example.com", i, i, i) + commands = append(commands, c) + } + commands = append(commands, ".exit") + + results := runner(commands, t) + + if results[1300] != "godb > Error: Table is full" { + t.Errorf("Our Tables max capacity is 1300 rows, but we're able to insert %v\n", len(results)) + } +} -- 2.54.0 From 9eeac4e7df5be695c1bb19ae0158d861b68c5f22 Mon Sep 17 00:00:00 2001 From: TakshakRamteke Date: Wed, 29 Jul 2026 00:35:03 +0530 Subject: [PATCH 3/6] fix: #9 no error on trying to insert more than limit, plus some other tests and minor fix --- internals/repl/repl.go | 4 ++++ internals/vm/vm.go | 50 +++++++++++++++++++++++++++++++++--------- tests/main_test.go | 28 +++++++++++++++++++++++ 3 files changed, 72 insertions(+), 10 deletions(-) diff --git a/internals/repl/repl.go b/internals/repl/repl.go index 4e2de24..79e6483 100644 --- a/internals/repl/repl.go +++ b/internals/repl/repl.go @@ -37,6 +37,10 @@ func Run() { fmt.Println("Syntax error, could not parse statement") case PrepareUnrecognised: fmt.Printf("Unrecognised keyword at the start of '%s'.\n", inputBuffer.Buffer) + case PrepareTooManyArgs: + fmt.Printf("Too many arguments in the insert statement '%v'\n", inputBuffer.Buffer) + case PrepareValueTooLong: + fmt.Printf("Values for either username or email are too long\n") } switch statement.ExecuteStatement(table) { diff --git a/internals/vm/vm.go b/internals/vm/vm.go index a9b93ab..dec21f6 100644 --- a/internals/vm/vm.go +++ b/internals/vm/vm.go @@ -4,6 +4,8 @@ import ( "encoding/binary" "fmt" "os" + "slices" + "strconv" "strings" . "godb/internals/buffer" @@ -21,6 +23,8 @@ type PrepareResults int const ( PrepareSuccess PrepareResults = iota PrepareUnrecognised + PrepareTooManyArgs + PrepareValueTooLong PrepareSyntaxError ) @@ -151,19 +155,45 @@ func DoMetaCommands(inputBuffer *InputBuffer) MetaCommandResults { return metacommand } +func (statement *Statement) perpareInsert(inputBuffer *InputBuffer) PrepareResults { + inputs := strings.Split(inputBuffer.Buffer, " ")[1:] + if len(inputs) < 3 { + return PrepareSyntaxError + } + if len(inputs) > 3 { + return PrepareTooManyArgs + } + if slices.Contains(inputs, "null") || slices.Contains(inputs, "NULL") || slices.Contains(inputs, "Null") || slices.Contains(inputs, " ") { + return PrepareSyntaxError + } + + tid, err := strconv.ParseUint(inputs[0], 10, 0) + if err != nil { + // fmt.Printf("supplied ID: %v isn't acceptable, please correct and retry\n", inputs[0]) + return PrepareSyntaxError + } + id := uint(tid) + if len(inputs[1]) > usernameSize { + // fmt.Printf("%v is too long for username\n", inputs[1]) + return PrepareValueTooLong + } + if len(inputs[2]) > emailSize { + // fmt.Printf("%v is too long for email\n", inputs[2]) + return PrepareValueTooLong + } + + statement.row.id = id + statement.row.username = inputs[1] + statement.row.email = inputs[2] + + statement.stype = StatementInsert + return PrepareSuccess +} + func (statement *Statement) PrepareStatements(inputBuffer *InputBuffer) PrepareResults { statement.stype = StatementUnrecognized if strings.HasPrefix(inputBuffer.Buffer, "insert") { - argsAssigned, err := fmt.Sscanf(inputBuffer.Buffer, "insert %d %s %s", &statement.row.id, &statement.row.username, &statement.row.email) - if err != nil { - fmt.Println("error parsing insert statement") - fmt.Println(err.Error()) - } - if err != nil || argsAssigned > 3 { - return PrepareSyntaxError - } - statement.stype = StatementInsert - return PrepareSuccess + return statement.perpareInsert(inputBuffer) } if inputBuffer.Buffer == "select" { statement.stype = StatementSelect diff --git a/tests/main_test.go b/tests/main_test.go index 029e0b7..94a3b6d 100644 --- a/tests/main_test.go +++ b/tests/main_test.go @@ -106,3 +106,31 @@ func TestTableFull(t *testing.T) { t.Errorf("Our Tables max capacity is 1300 rows, but we're able to insert %v\n", len(results)) } } + +func TestMaxLengthForValues(t *testing.T) { + commands := []string{"insert 0 " + strings.Repeat("n", 32) + " " + strings.Repeat("n", 255), ".exit"} + expected := []string{"godb > Executed.", "godb > bye!"} + + results := runner(commands, t) + for i := range results { + results[i] = strings.ReplaceAll(results[i], "\n", "") + } + + if !slices.Equal(expected, results) { + t.Errorf("Output Mismatch\nExpected: %v\nGot: %v\n", expected, results) + } +} + +func TestOverMaxLengthValues(t *testing.T) { + commands := []string{"insert 0 " + strings.Repeat("n", 42) + " " + strings.Repeat("n", 275), ".exit"} + expected := []string{"godb > Values for either username or email are too long", "godb > bye!"} + + results := runner(commands, t) + for i := range results { + results[i] = strings.ReplaceAll(results[i], "\n", "") + } + + if !slices.Equal(expected, results) { + t.Errorf("Output Mismatch\nExpected: %v\nGot: %v\n", expected, results) + } +} -- 2.54.0 From c296945f0ea98c6a556ada3e87e70672957033ea Mon Sep 17 00:00:00 2001 From: TakshakRamteke Date: Wed, 29 Jul 2026 00:51:30 +0530 Subject: [PATCH 4/6] feat: restructured test to be more idiomatic --- tests/main_test.go | 76 +++++++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 45 deletions(-) diff --git a/tests/main_test.go b/tests/main_test.go index 94a3b6d..eca49e2 100644 --- a/tests/main_test.go +++ b/tests/main_test.go @@ -66,28 +66,42 @@ func runner(commands []string, t *testing.T) []string { return results } -func TestMain(t *testing.T) { - commands := []string{ - "insert 1 user1 person1@example.com", - "select", - ".exit", +func TestE2E(t *testing.T) { + tests := []struct { + name string + commands []string + expected []string + }{ + { + name: "Test Main Loop", + commands: []string{"insert 1 user1 person1@example.com", "select", ".exit"}, + expected: []string{"godb > Executed.", "godb > {1 user1 person1@example.com}", "Executed.", "godb > bye!"}, + }, + { + name: "Test Maximum Length For Values", + commands: []string{"insert 0 " + strings.Repeat("n", 32) + " " + strings.Repeat("n", 255), ".exit"}, + expected: []string{"godb > Executed.", "godb > bye!"}, + }, + { + name: "Test Over Maximum Length For Values", + commands: []string{"insert 0 " + strings.Repeat("n", 42) + " " + strings.Repeat("n", 275), ".exit"}, + expected: []string{"godb > Values for either username or email are too long", "godb > bye!"}, + }, } - expected := []string{ - "godb > Executed.", - "godb > {1 user1 person1@example.com}", - "Executed.", - "godb > bye!", - } - results := runner(commands, t) + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + results := runner(test.commands, t) + for i := range results { + results[i] = strings.ReplaceAll(results[i], "\n", "") + } - for i := range results { - results[i] = strings.ReplaceAll(results[i], "\n", "") + if !slices.Equal(test.expected, results) { + t.Errorf("Output Mismatch\nExpected: %v\nGot: %v\n", test.expected, results) + } + }) } - if !slices.Equal(expected, results) { - t.Errorf("Output Mismatch\nExpected: %v\nGot: %v\n", expected, results) - } } func TestTableFull(t *testing.T) { @@ -106,31 +120,3 @@ func TestTableFull(t *testing.T) { t.Errorf("Our Tables max capacity is 1300 rows, but we're able to insert %v\n", len(results)) } } - -func TestMaxLengthForValues(t *testing.T) { - commands := []string{"insert 0 " + strings.Repeat("n", 32) + " " + strings.Repeat("n", 255), ".exit"} - expected := []string{"godb > Executed.", "godb > bye!"} - - results := runner(commands, t) - for i := range results { - results[i] = strings.ReplaceAll(results[i], "\n", "") - } - - if !slices.Equal(expected, results) { - t.Errorf("Output Mismatch\nExpected: %v\nGot: %v\n", expected, results) - } -} - -func TestOverMaxLengthValues(t *testing.T) { - commands := []string{"insert 0 " + strings.Repeat("n", 42) + " " + strings.Repeat("n", 275), ".exit"} - expected := []string{"godb > Values for either username or email are too long", "godb > bye!"} - - results := runner(commands, t) - for i := range results { - results[i] = strings.ReplaceAll(results[i], "\n", "") - } - - if !slices.Equal(expected, results) { - t.Errorf("Output Mismatch\nExpected: %v\nGot: %v\n", expected, results) - } -} -- 2.54.0 From 27f9403432d279d2c08f50f84512b2ca966dc02f Mon Sep 17 00:00:00 2001 From: TakshakRamteke Date: Wed, 29 Jul 2026 01:04:00 +0530 Subject: [PATCH 5/6] feat: converted unit tests for vm to idiomatic table driven tests --- internals/vm/vm_test.go | 68 +++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 30 deletions(-) diff --git a/internals/vm/vm_test.go b/internals/vm/vm_test.go index e1701ed..82c7597 100644 --- a/internals/vm/vm_test.go +++ b/internals/vm/vm_test.go @@ -15,36 +15,44 @@ func comapareExpectedAndRetruned(t *testing.T, result, expectedResult PrepareRes } } -func TestPrepareStatementSelect(t *testing.T) { - inputBuffer.Buffer = "select" +func TestVM(t *testing.T) { + tests := []struct { + name string + input string + prepareStatusExpected PrepareResults + statementResultExpected statementType + }{ + { + name: "Testing Select Statement", + input: "select", + prepareStatusExpected: PrepareSuccess, + statementResultExpected: StatementSelect, + }, + { + name: "Testing Insert Statement", + input: "insert 0 hello hello.com", + prepareStatusExpected: PrepareSuccess, + statementResultExpected: StatementInsert, + }, + { + name: "Testing Unrecognized Statement", + input: "something", + prepareStatusExpected: PrepareUnrecognised, + statementResultExpected: StatementUnrecognized, + }, + { + name: "Testing Syntax Errors", + input: "insert 0 hello", + prepareStatusExpected: PrepareSyntaxError, + statementResultExpected: StatementUnrecognized, + }, + } - result := statement.PrepareStatements(inputBuffer) - - comapareExpectedAndRetruned(t, result, PrepareSuccess, statement.stype, StatementSelect) -} - -func TestPrepareStatementInsert(t *testing.T) { - inputBuffer.Buffer = "insert 0 hello hello.com" - - result := statement.PrepareStatements(inputBuffer) - - comapareExpectedAndRetruned(t, result, PrepareSuccess, statement.stype, StatementInsert) -} - -func TestPrepareStatementUnrecognized(t *testing.T) { - inputBuffer.Buffer = "something" - - result := statement.PrepareStatements(inputBuffer) - - comapareExpectedAndRetruned(t, result, PrepareUnrecognised, statement.stype, StatementUnrecognized) -} - -func TestPrepareStatementSyntaxError(t *testing.T) { - inputBuffer.Buffer = "insert 0 hello" - - result := statement.PrepareStatements(inputBuffer) - - if result != PrepareSyntaxError { - t.Errorf("Expected prepareResult to be: %v, got: %v\n", PrepareSyntaxError, result) + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + inputBuffer.Buffer = test.input + result := statement.PrepareStatements(inputBuffer) + comapareExpectedAndRetruned(t, result, test.prepareStatusExpected, statement.stype, test.statementResultExpected) + }) } } -- 2.54.0 From 7c4705d2787df2d1d0c60e433925ca00d66b0ec3 Mon Sep 17 00:00:00 2001 From: TakshakRamteke Date: Wed, 29 Jul 2026 01:07:55 +0530 Subject: [PATCH 6/6] fix: removed unnecessary functions for vm unit test --- internals/vm/vm_test.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/internals/vm/vm_test.go b/internals/vm/vm_test.go index 82c7597..40d8677 100644 --- a/internals/vm/vm_test.go +++ b/internals/vm/vm_test.go @@ -9,12 +9,6 @@ var inputBuffer = buffer.NewInputBuffer() var statement = NewStatement() -func comapareExpectedAndRetruned(t *testing.T, result, expectedResult PrepareResults, resultingStype, expectedStype statementType) { - if result != expectedResult && resultingStype == expectedStype { - t.Errorf("Expected prepareResult and stype to be: (%v, %v), got: (%v,%v)\n", expectedResult, expectedStype, result, resultingStype) - } -} - func TestVM(t *testing.T) { tests := []struct { name string @@ -52,7 +46,9 @@ func TestVM(t *testing.T) { t.Run(test.name, func(t *testing.T) { inputBuffer.Buffer = test.input result := statement.PrepareStatements(inputBuffer) - comapareExpectedAndRetruned(t, result, test.prepareStatusExpected, statement.stype, test.statementResultExpected) + if result != test.prepareStatusExpected && statement.stype == test.statementResultExpected { + t.Errorf("Expected prepareResult and stype to be: (%v, %v), got: (%v,%v)\n", test.prepareStatusExpected, test.statementResultExpected, result, statement.stype) + } }) } } -- 2.54.0