test: Rewrote expect checks to use expect_value where appropriate.

This commit is contained in:
2026-06-25 10:35:00 -04:00
parent 0b5bf4db73
commit ad3de74e35
11 changed files with 126 additions and 179 deletions

View File

@@ -1,6 +1,6 @@
# TODOs # TODOs
1. Commands are still leaking. (Do 15. first) 1. Commands are still leaking. (Do 13. first)
2. Add color flag and support non colored output. 2. Add color flag and support non colored output.
@@ -8,31 +8,29 @@
4. Generate md and man pages again. 4. Generate md and man pages again.
6. Consistently ignore allocator errors 5. Consistently ignore allocator errors
7. Check for prealloc opportunities. i.e. `make([dynamic]string)` -> `make([dynamic]string, 5)`. 6. Check for prealloc opportunities. i.e. `make([dynamic]string)` -> `make([dynamic]string, 5)`.
8. Add a text filter to the multi_select. 7. Add a text filter to the multi_select.
9. Add tests for untested commands. 8. Add tests for untested commands.
10. add --format -f flag to commands that draw tables. 9. add --format -f flag to commands that draw tables.
11. Replace `testing.expect` calls with `testing.expect_value` calls where appropriate. 10. procedures should be ordered by use, main at the top, then in the order they are called from main.
12. procedures should be ordered by use, main at the top, then in the order they are called from main. 11. Shell completion
13. Shell completion 12. Bring back windows support / cross-compilation.
14. Bring back windows support / cross-compilation. 13. Test all cmds / terminal branches.
15. Test all cmds / terminal branches. 14. Fix error messages to use fmt.eprintf (stderr) instead of fmt.printf (stdout)
16. Fix error messages to use fmt.eprintf (stderr) instead of fmt.printf (stdout) 15. Pass allocator to findr?
17. Pass allocator to findr? 16. Update `read_wire_string` to use a slice.
18. Update `read_wire_string` to use a slice.
## Double-check AI output ## Double-check AI output

View File

@@ -123,7 +123,7 @@ test_command_help_unknown :: proc(t: ^testing.T) {
text := strings.to_string(b) text := strings.to_string(b)
testing.expect_value(t, len(text), 0) testing.expect_value(t, len(text), 0)
} }
@(test) @(test)
test_command_help_version :: proc(t: ^testing.T) { test_command_help_version :: proc(t: ^testing.T) {
@@ -236,9 +236,9 @@ test_parse_args_positional :: proc(t: ^testing.T) {
testing.expect(t, ok, "should succeed") testing.expect(t, ok, "should succeed")
testing.expect_value(t, cmd.name, "backup") testing.expect_value(t, cmd.name, "backup")
testing.expect(t, len(cmd.args) == 1) testing.expect_value(t, len(cmd.args), 1)
testing.expect(t, cmd.args[0] == "/project/.env") testing.expect_value(t, cmd.args[0], "/project/.env")
} }
@(test) @(test)
test_parse_args_long_flag_with_value :: proc(t: ^testing.T) { test_parse_args_long_flag_with_value :: proc(t: ^testing.T) {
@@ -248,7 +248,7 @@ test_parse_args_long_flag_with_value :: proc(t: ^testing.T) {
defer delete_command(&cmd) defer delete_command(&cmd)
testing.expect_value(t, cmd.flags["config"], "x.json") testing.expect_value(t, cmd.flags["config"], "x.json")
} }
@(test) @(test)
test_parse_args_short_flag_with_value :: proc(t: ^testing.T) { test_parse_args_short_flag_with_value :: proc(t: ^testing.T) {
@@ -258,7 +258,7 @@ test_parse_args_short_flag_with_value :: proc(t: ^testing.T) {
defer delete_command(&cmd) defer delete_command(&cmd)
testing.expect_value(t, cmd.flags["c"], "x.json") testing.expect_value(t, cmd.flags["c"], "x.json")
} }
@(test) @(test)
test_parse_args_long_bool_flag :: proc(t: ^testing.T) { test_parse_args_long_bool_flag :: proc(t: ^testing.T) {
@@ -268,7 +268,7 @@ test_parse_args_long_bool_flag :: proc(t: ^testing.T) {
defer delete_command(&cmd) defer delete_command(&cmd)
testing.expect_value(t, cmd.bool_set["force"], true) testing.expect_value(t, cmd.bool_set["force"], true)
} }
@(test) @(test)
test_parse_args_short_bool_flag :: proc(t: ^testing.T) { test_parse_args_short_bool_flag :: proc(t: ^testing.T) {
@@ -278,7 +278,7 @@ test_parse_args_short_bool_flag :: proc(t: ^testing.T) {
defer delete_command(&cmd) defer delete_command(&cmd)
testing.expect_value(t, cmd.bool_set["l"], true) testing.expect_value(t, cmd.bool_set["l"], true)
} }
@(test) @(test)
test_parse_args_multiple_positionals :: proc(t: ^testing.T) { test_parse_args_multiple_positionals :: proc(t: ^testing.T) {
@@ -288,9 +288,9 @@ test_parse_args_multiple_positionals :: proc(t: ^testing.T) {
defer delete_command(&cmd) defer delete_command(&cmd)
testing.expect_value(t, len(cmd.args), 2) testing.expect_value(t, len(cmd.args), 2)
testing.expect(t, cmd.args[0] == "a") testing.expect_value(t, cmd.args[0], "a")
testing.expect(t, cmd.args[1] == "b") testing.expect_value(t, cmd.args[1], "b")
} }
@(test) @(test)
test_parse_args_mixed_flags_and_positionals :: proc(t: ^testing.T) { test_parse_args_mixed_flags_and_positionals :: proc(t: ^testing.T) {
@@ -300,9 +300,9 @@ test_parse_args_mixed_flags_and_positionals :: proc(t: ^testing.T) {
defer delete_command(&cmd) defer delete_command(&cmd)
testing.expect_value(t, cmd.bool_set["force"], true) testing.expect_value(t, cmd.bool_set["force"], true)
testing.expect(t, len(cmd.args) == 1) testing.expect_value(t, len(cmd.args), 1)
testing.expect(t, cmd.args[0] == "/project/.env") testing.expect_value(t, cmd.args[0], "/project/.env")
} }
@(test) @(test)
test_parse_args_no_args :: proc(t: ^testing.T) { test_parse_args_no_args :: proc(t: ^testing.T) {
@@ -318,10 +318,10 @@ test_parse_args_flag_then_positional_then_flag :: proc(t: ^testing.T) {
testing.expect(t, ok, "should succeed") testing.expect(t, ok, "should succeed")
testing.expect_value(t, cmd.bool_set["force"], true) testing.expect_value(t, cmd.bool_set["force"], true)
testing.expect(t, cmd.bool_set["verbose"] == true) testing.expect_value(t, cmd.bool_set["verbose"], true)
testing.expect(t, len(cmd.args) == 1) testing.expect_value(t, len(cmd.args), 1)
testing.expect(t, cmd.args[0] == "a.env") testing.expect_value(t, cmd.args[0], "a.env")
} }
@(test) @(test)
test_parse_args_config_file_long_flag :: proc(t: ^testing.T) { test_parse_args_config_file_long_flag :: proc(t: ^testing.T) {
@@ -333,11 +333,7 @@ test_parse_args_config_file_long_flag :: proc(t: ^testing.T) {
defer delete_command(&cmd) defer delete_command(&cmd)
testing.expect_value(t, cmd.config_path, "/custom/config.json") testing.expect_value(t, cmd.config_path, "/custom/config.json")
t, }
cmd.config_path == "/custom/config.json",
"config_path should be set from --config-file",
)
}
@(test) @(test)
test_parse_args_config_file_short_flag :: proc(t: ^testing.T) { test_parse_args_config_file_short_flag :: proc(t: ^testing.T) {
@@ -347,11 +343,7 @@ test_parse_args_config_file_short_flag :: proc(t: ^testing.T) {
defer delete_command(&cmd) defer delete_command(&cmd)
testing.expect_value(t, cmd.config_path, "/custom/config.json") testing.expect_value(t, cmd.config_path, "/custom/config.json")
t, }
cmd.config_path == "/custom/config.json",
"config_path should be set from -c",
)
}
@(test) @(test)
test_parse_args_config_file_defaults :: proc(t: ^testing.T) { test_parse_args_config_file_defaults :: proc(t: ^testing.T) {

View File

@@ -1,7 +1,6 @@
#+test #+test
package main package main
import "core:fmt"
import "core:testing" import "core:testing"
@(test) @(test)
@@ -10,13 +9,9 @@ test_find_unbacked_finds_missing :: proc(t: ^testing.T) {
db := []EnvFile{{path = "/a/.env"}, {path = "/b/.env"}} db := []EnvFile{{path = "/a/.env"}, {path = "/b/.env"}}
result := find_unbacked(local, db[:]) result := find_unbacked(local, db[:])
testing.expect(t, len(result) == 1, fmt.tprintf("expected 1 unbacked, got %d", len(result))) testing.expect_value(t, len(result), 1)
if len(result) > 0 { if len(result) > 0 {
testing.expect( testing.expect_value(t, result[0], "/c/.env")
t,
result[0] == "/c/.env",
fmt.tprintf("expected /c/.env, got %s", result[0]),
)
} }
} }
@@ -26,7 +21,7 @@ test_find_unbacked_all_backed :: proc(t: ^testing.T) {
db := []EnvFile{{path = "/a/.env"}, {path = "/b/.env"}} db := []EnvFile{{path = "/a/.env"}, {path = "/b/.env"}}
result := find_unbacked(local, db[:]) result := find_unbacked(local, db[:])
testing.expect(t, len(result) == 0, fmt.tprintf("expected 0 unbacked, got %d", len(result))) testing.expect_value(t, len(result), 0)
} }
@(test) @(test)
@@ -35,7 +30,7 @@ test_find_unbacked_no_local :: proc(t: ^testing.T) {
db := []EnvFile{{path = "/a/.env"}} db := []EnvFile{{path = "/a/.env"}}
result := find_unbacked(local, db[:]) result := find_unbacked(local, db[:])
testing.expect(t, len(result) == 0, fmt.tprintf("expected 0 unbacked, got %d", len(result))) testing.expect_value(t, len(result), 0)
} }
@(test) @(test)
@@ -44,6 +39,6 @@ test_find_unbacked_none_backed :: proc(t: ^testing.T) {
db: []EnvFile db: []EnvFile
result := find_unbacked(local, db[:]) result := find_unbacked(local, db[:])
testing.expect(t, len(result) == 2, fmt.tprintf("expected 2 unbacked, got %d", len(result))) testing.expect_value(t, len(result), 2)
} }

View File

@@ -11,9 +11,9 @@ test_filepath_base_equals_rel :: proc(t: ^testing.T) {
for path in cases { for path in cases {
dir := filepath.dir(path) dir := filepath.dir(path)
rel, rel_err := filepath.rel(dir, path, context.temp_allocator) rel, rel_err := filepath.rel(dir, path, context.temp_allocator)
testing.expect(t, rel_err == nil, "filepath.rel returned an error") testing.expect_value(t, rel_err, nil)
base := filepath.base(path) base := filepath.base(path)
testing.expect(t, rel == base, "filepath.rel(dir, path) should equal filepath.base(path)") testing.expect_value(t, rel, base)
} }
} }

View File

@@ -16,13 +16,9 @@ test_new_config_single_key :: proc(t: ^testing.T) {
cfg := new_config(paths) cfg := new_config(paths)
defer delete_config(&cfg) defer delete_config(&cfg)
testing.expect(t, len(cfg.keys) == 1, "should have 1 key") testing.expect_value(t, len(cfg.keys), 1)
testing.expect(t, cfg.keys[0].private == "/home/user/.ssh/id_ed25519", "Private path mismatch") testing.expect_value(t, cfg.keys[0].private, "/home/user/.ssh/id_ed25519")
testing.expect( testing.expect_value(t, cfg.keys[0].public, "/home/user/.ssh/id_ed25519.pub")
t,
cfg.keys[0].public == "/home/user/.ssh/id_ed25519.pub",
"Public path mismatch",
)
} }
@(test) @(test)
@@ -31,9 +27,9 @@ test_new_config_multiple_keys :: proc(t: ^testing.T) {
cfg := new_config(paths) cfg := new_config(paths)
defer delete_config(&cfg) defer delete_config(&cfg)
testing.expect(t, len(cfg.keys) == 2, "should have 2 keys") testing.expect_value(t, len(cfg.keys), 2)
testing.expect(t, cfg.keys[0].private == "/home/user/.ssh/id_ed25519") testing.expect_value(t, cfg.keys[0].private, "/home/user/.ssh/id_ed25519")
testing.expect(t, cfg.keys[1].private == "/home/user/.ssh/id_rsa") testing.expect_value(t, cfg.keys[1].private, "/home/user/.ssh/id_rsa")
} }
@(test) @(test)
@@ -42,7 +38,7 @@ test_new_config_empty_keys :: proc(t: ^testing.T) {
cfg := new_config(paths) cfg := new_config(paths)
defer delete_config(&cfg) defer delete_config(&cfg)
testing.expect(t, len(cfg.keys) == 0, "should have 0 keys") testing.expect_value(t, len(cfg.keys), 0)
} }
@(test) @(test)
@@ -51,10 +47,10 @@ test_new_config_scan_defaults :: proc(t: ^testing.T) {
cfg := new_config(paths) cfg := new_config(paths)
defer delete_config(&cfg) defer delete_config(&cfg)
testing.expect(t, cfg.scan_config.matcher == "\\.env", "matcher should be \\.env") testing.expect_value(t, cfg.scan_config.matcher, "\\.env")
testing.expect(t, len(cfg.scan_config.exclude) == 4, "should have 4 exclude patterns") testing.expect_value(t, len(cfg.scan_config.exclude), 4)
testing.expect(t, len(cfg.scan_config.include) == 1, "should have 1 include path") testing.expect_value(t, len(cfg.scan_config.include), 1)
testing.expect(t, cfg.scan_config.include[0] == "~", "include should be ~") testing.expect_value(t, cfg.scan_config.include[0], "~")
} }
@(test) @(test)
@@ -65,7 +61,7 @@ test_new_config_exclude_patterns :: proc(t: ^testing.T) {
expected := []string{"*\\.envrc", "\\.local/", "node_modules", "vendor"} expected := []string{"*\\.envrc", "\\.local/", "node_modules", "vendor"}
for i in 0 ..< len(expected) { for i in 0 ..< len(expected) {
testing.expect(t, cfg.scan_config.exclude[i] == expected[i]) testing.expect_value(t, cfg.scan_config.exclude[i], expected[i])
} }
} }
@@ -75,7 +71,7 @@ test_save_load_config_roundtrip :: proc(t: ^testing.T) {
defer os.remove_all(base) defer os.remove_all(base)
cfgPath, err := filepath.join([]string{base, "config.json"}, context.temp_allocator) cfgPath, err := filepath.join([]string{base, "config.json"}, context.temp_allocator)
testing.expect(t, err == nil, "cfgPath should build successfully") testing.expect_value(t, err, nil)
cfg := new_config([]string{"/home/user/.ssh/id_ed25519"}, cfgPath) cfg := new_config([]string{"/home/user/.ssh/id_ed25519"}, cfgPath)
defer delete_config(&cfg) defer delete_config(&cfg)
@@ -87,13 +83,13 @@ test_save_load_config_roundtrip :: proc(t: ^testing.T) {
if !ok do return if !ok do return
defer delete_config(&loaded) defer delete_config(&loaded)
testing.expect(t, len(loaded.keys) == 1, "should have 1 key") testing.expect_value(t, len(loaded.keys), 1)
testing.expect(t, loaded.keys[0].private == "/home/user/.ssh/id_ed25519") testing.expect_value(t, loaded.keys[0].private, "/home/user/.ssh/id_ed25519")
testing.expect(t, loaded.keys[0].public == "/home/user/.ssh/id_ed25519.pub") testing.expect_value(t, loaded.keys[0].public, "/home/user/.ssh/id_ed25519.pub")
testing.expect(t, loaded.scan_config.matcher == "\\.env") testing.expect_value(t, loaded.scan_config.matcher, "\\.env")
testing.expect(t, len(loaded.scan_config.exclude) == 4) testing.expect_value(t, len(loaded.scan_config.exclude), 4)
testing.expect(t, len(loaded.scan_config.include) == 1) testing.expect_value(t, len(loaded.scan_config.include), 1)
testing.expect(t, loaded.scan_config.include[0] == "~") testing.expect_value(t, loaded.scan_config.include[0], "~")
} }
@(test) @(test)
@@ -108,7 +104,7 @@ test_save_config_no_clobber :: proc(t: ^testing.T) {
defer os.remove_all(base) defer os.remove_all(base)
cfgPath, err := filepath.join([]string{base, "config.json"}, context.temp_allocator) cfgPath, err := filepath.join([]string{base, "config.json"}, context.temp_allocator)
testing.expect(t, err == nil, "cfgPath should build successfully") testing.expect_value(t, err, nil)
cfg := new_config([]string{"/home/user/.ssh/key1"}, cfgPath) cfg := new_config([]string{"/home/user/.ssh/key1"}, cfgPath)
defer delete_config(&cfg) defer delete_config(&cfg)
@@ -125,7 +121,7 @@ test_save_config_force_overwrites :: proc(t: ^testing.T) {
defer os.remove_all(base) defer os.remove_all(base)
cfgPath, err := filepath.join([]string{base, "config.json"}, context.temp_allocator) cfgPath, err := filepath.join([]string{base, "config.json"}, context.temp_allocator)
testing.expect(t, err == nil, "cfgPath should build successfully") testing.expect_value(t, err, nil)
cfg := new_config([]string{"/home/user/.ssh/key1"}, cfgPath) cfg := new_config([]string{"/home/user/.ssh/key1"}, cfgPath)
defer delete_config(&cfg) defer delete_config(&cfg)
@@ -140,12 +136,8 @@ test_save_config_force_overwrites :: proc(t: ^testing.T) {
if !ok do return if !ok do return
defer delete_config(&loaded) defer delete_config(&loaded)
testing.expect(t, len(loaded.keys) == 1, "should have 1 key") testing.expect_value(t, len(loaded.keys), 1)
testing.expect( testing.expect_value(t, loaded.keys[0].private, "/home/user/.ssh/key2")
t,
loaded.keys[0].private == "/home/user/.ssh/key2",
"should be the overwritten key",
)
} }
@(test) @(test)
@@ -190,7 +182,7 @@ test_search_paths_expands_tilde :: proc(t: ^testing.T) {
paths := search_paths(cfg, context.temp_allocator) paths := search_paths(cfg, context.temp_allocator)
testing.expect(t, len(paths) == 1, "should have 1 path") testing.expect_value(t, len(paths), 1)
if len(paths) > 0 { if len(paths) > 0 {
testing.expectf( testing.expectf(
t, t,

View File

@@ -27,13 +27,10 @@ test_encrypt_decrypt_roundtrip :: proc(t: ^testing.T) {
testing.expect(t, dec_ok, "decryption should succeed") testing.expect(t, dec_ok, "decryption should succeed")
defer delete(decrypted) defer delete(decrypted)
testing.expect( testing.expect_value(t, len(decrypted), len(original))
t, // TODO: Should this be a loop?
len(decrypted) == len(original),
fmt.tprintf("expected %d bytes, got %d", len(original), len(decrypted)),
)
for i in 0 ..< len(original) { for i in 0 ..< len(original) {
testing.expect(t, decrypted[i] == original[i], fmt.tprintf("byte mismatch at index %d", i)) testing.expect_value(t, decrypted[i], original[i])
} }
} }
@@ -56,16 +53,8 @@ test_encrypt_decrypt_multi_recipient :: proc(t: ^testing.T) {
defer delete(decrypted2) defer delete(decrypted2)
for i in 0 ..< len(original) { for i in 0 ..< len(original) {
testing.expect( testing.expect_value(t, decrypted1[i], original[i])
t, testing.expect_value(t, decrypted2[i], original[i])
decrypted1[i] == original[i],
fmt.tprintf("key1: byte mismatch at %d", i),
)
testing.expect(
t,
decrypted2[i] == original[i],
fmt.tprintf("key2: byte mismatch at %d", i),
)
} }
} }
@@ -96,7 +85,7 @@ test_encrypt_empty_plaintext :: proc(t: ^testing.T) {
testing.expect(t, dec_ok, "decryption should succeed") testing.expect(t, dec_ok, "decryption should succeed")
defer delete(decrypted) defer delete(decrypted)
testing.expect(t, len(decrypted) == 0, "decrypted empty data should be empty") testing.expect_value(t, len(decrypted), 0)
} }
@(test) @(test)
@@ -113,8 +102,9 @@ test_recipient_can_decrypt_senders_data :: proc(t: ^testing.T) {
testing.expect(t, dec_ok, "second recipient should decrypt without the sender key present") testing.expect(t, dec_ok, "second recipient should decrypt without the sender key present")
defer delete(decrypted) defer delete(decrypted)
// TODO: Should this be a loop?
for i in 0 ..< len(original) { for i in 0 ..< len(original) {
testing.expect(t, decrypted[i] == original[i], fmt.tprintf("byte mismatch at %d", i)) testing.expect_value(t, decrypted[i], original[i])
} }
} }
@@ -128,9 +118,9 @@ test_ciphertext_has_magic :: proc(t: ^testing.T) {
defer delete(encrypted) defer delete(encrypted)
testing.expect(t, len(encrypted) >= 4, "ciphertext should have at least 4 bytes") testing.expect(t, len(encrypted) >= 4, "ciphertext should have at least 4 bytes")
testing.expect(t, encrypted[0] == u8('E'), "magic byte 0") testing.expect_value(t, encrypted[0], u8('E'))
testing.expect(t, encrypted[1] == u8('N'), "magic byte 1") testing.expect_value(t, encrypted[1], u8('N'))
testing.expect(t, encrypted[2] == u8('V'), "magic byte 2") testing.expect_value(t, encrypted[2], u8('V'))
testing.expect(t, encrypted[3] == u8('R'), "magic byte 3") testing.expect_value(t, encrypted[3], u8('R'))
} }

View File

@@ -67,10 +67,10 @@ test_encrypt_decrypt_sqlite_roundtrip :: proc(t: ^testing.T) {
defer delete(encrypted) defer delete(encrypted)
testing.expect(t, len(encrypted) >= HEADER_SIZE, "ciphertext should have header") testing.expect(t, len(encrypted) >= HEADER_SIZE, "ciphertext should have header")
testing.expect(t, encrypted[0] == u8('E'), "magic byte 0") testing.expect_value(t, encrypted[0], u8('E'))
testing.expect(t, encrypted[1] == u8('N'), "magic byte 1") testing.expect_value(t, encrypted[1], u8('N'))
testing.expect(t, encrypted[2] == u8('V'), "magic byte 2") testing.expect_value(t, encrypted[2], u8('V'))
testing.expect(t, encrypted[3] == u8('R'), "magic byte 3") testing.expect_value(t, encrypted[3], u8('R'))
plaintext, dec_ok := decrypt(encrypted, cfg.keys[:]) plaintext, dec_ok := decrypt(encrypted, cfg.keys[:])
testing.expect(t, dec_ok, "decryption should succeed") testing.expect(t, dec_ok, "decryption should succeed")
@@ -142,7 +142,7 @@ test_encrypt_write_read_decrypt :: proc(t: ^testing.T) {
} }
defer delete(plaintext) defer delete(plaintext)
testing.expect(t, len(plaintext) == len(sqlite_data), "size mismatch after file round-trip") testing.expect_value(t, len(plaintext), len(sqlite_data))
} }
@(test) @(test)
@@ -189,7 +189,7 @@ test_decrypt_then_deserialize_sqlite :: proc(t: ^testing.T) {
copy(buf[:len(plaintext)], plaintext) copy(buf[:len(plaintext)], plaintext)
rc = sqlite.deserialize(mem_db, "main", buf, n, n, {.FREEONCLOSE, .RESIZEABLE}) rc = sqlite.deserialize(mem_db, "main", buf, n, n, {.FREEONCLOSE, .RESIZEABLE})
testing.expect(t, rc == sqlite.OK, "deserialize should succeed") testing.expect_value(t, rc, sqlite.OK)
if rc != sqlite.OK { if rc != sqlite.OK {
sqlite.free(buf) sqlite.free(buf)
return return
@@ -198,14 +198,14 @@ test_decrypt_then_deserialize_sqlite :: proc(t: ^testing.T) {
sql: cstring = "SELECT path FROM envr_env_files" sql: cstring = "SELECT path FROM envr_env_files"
stmt: sqlite.Stmt stmt: sqlite.Stmt
rc = sqlite.prepare_v2(mem_db, sql, -1, &stmt, nil) rc = sqlite.prepare_v2(mem_db, sql, -1, &stmt, nil)
testing.expect(t, rc == sqlite.OK, "prepare failed") testing.expect_value(t, rc, sqlite.OK)
if rc != sqlite.OK { if rc != sqlite.OK {
return return
} }
defer sqlite.finalize(stmt) defer sqlite.finalize(stmt)
rc = sqlite.step(stmt) rc = sqlite.step(stmt)
testing.expect(t, rc == sqlite.ROW, "expected at least one row") testing.expect_value(t, rc, sqlite.ROW)
if rc == sqlite.ROW { if rc == sqlite.ROW {
path := string(sqlite.column_text(stmt, 0)) path := string(sqlite.column_text(stmt, 0))
testing.expect(t, len(path) > 0, "path should not be empty") testing.expect(t, len(path) > 0, "path should not be empty")
@@ -275,15 +275,7 @@ test_full_db_cycle :: proc(t: ^testing.T) {
} }
defer delete(plaintext2) defer delete(plaintext2)
testing.expect( testing.expect_value(t, len(plaintext2), len(original_data))
t,
len(plaintext2) == len(original_data),
fmt.tprintf(
"double round-trip size mismatch: expected %d, got %d",
len(original_data),
len(plaintext2),
),
)
os.remove(data_path) os.remove(data_path)
os.remove(envr_dir_path) os.remove(envr_dir_path)
@@ -317,7 +309,7 @@ test_ssh_key_parse_from_fixtures :: proc(t: ^testing.T) {
return return
} }
testing.expect(t, len(x25519_pairs) == 1, "should have 1 x25519 keypair") testing.expect_value(t, len(x25519_pairs), 1)
} }
@(test) @(test)
@@ -327,7 +319,7 @@ test_config_load_with_fixture_key :: proc(t: ^testing.T) {
delete(cfg.keys) delete(cfg.keys)
} }
testing.expect(t, len(cfg.keys) == 1, "should have 1 key") testing.expect_value(t, len(cfg.keys), 1)
key := cfg.keys[0] key := cfg.keys[0]

View File

@@ -81,7 +81,7 @@ test_db_insert_or_replace :: proc(t: ^testing.T) {
results, list_ok := db_list(&db) results, list_ok := db_list(&db)
testing.expect(t, list_ok, "list should succeed") testing.expect(t, list_ok, "list should succeed")
testing.expect(t, len(results) == 1, "should have 1 row, not 2") testing.expect_value(t, len(results), 1)
fetched, fetch_ok := db_fetch(&db, "/project/.env") fetched, fetch_ok := db_fetch(&db, "/project/.env")
testing.expect(t, fetch_ok, "fetch should succeed") testing.expect(t, fetch_ok, "fetch should succeed")
@@ -149,7 +149,7 @@ test_db_list_empty :: proc(t: ^testing.T) {
results, list_ok := db_list(&db) results, list_ok := db_list(&db)
testing.expect(t, list_ok, "list should succeed on empty db") testing.expect(t, list_ok, "list should succeed on empty db")
testing.expect(t, len(results) == 0, "should have 0 rows") testing.expect_value(t, len(results), 0)
} }
@(test) @(test)
@@ -276,11 +276,11 @@ test_get_git_remotes_single :: proc(t: ^testing.T) {
config_content := "[core]\n\trepositoryformatversion = 0\n[remote \"origin\"]\n\turl = git@github.com:user/repo.git\n\tfetch = +refs/heads/*:refs/remotes/origin/*\n" config_content := "[core]\n\trepositoryformatversion = 0\n[remote \"origin\"]\n\turl = git@github.com:user/repo.git\n\tfetch = +refs/heads/*:refs/remotes/origin/*\n"
config_path := fmt.tprintf("%s/config", git_dir) config_path := fmt.tprintf("%s/config", git_dir)
err := os.write_entire_file(config_path, transmute([]u8)config_content) err := os.write_entire_file(config_path, transmute([]u8)config_content)
testing.expect(t, err == nil, "should write .git/config") testing.expect_value(t, err, nil)
remotes := get_git_remotes(base, context.temp_allocator) remotes := get_git_remotes(base, context.temp_allocator)
testing.expect(t, len(remotes) == 1, "should find 1 remote") testing.expect_value(t, len(remotes), 1)
if len(remotes) != 1 do return if len(remotes) != 1 do return
testing.expect_value(t, remotes[0], "git@github.com:user/repo.git") testing.expect_value(t, remotes[0], "git@github.com:user/repo.git")
} }
@@ -296,11 +296,11 @@ test_get_git_remotes_multiple :: proc(t: ^testing.T) {
config_content := "[remote \"origin\"]\n\turl = git@github.com:user/repo.git\n[remote \"upstream\"]\n\turl = https://gitlab.com/upstream/repo.git\n" config_content := "[remote \"origin\"]\n\turl = git@github.com:user/repo.git\n[remote \"upstream\"]\n\turl = https://gitlab.com/upstream/repo.git\n"
config_path := fmt.tprintf("%s/config", git_dir) config_path := fmt.tprintf("%s/config", git_dir)
err := os.write_entire_file(config_path, transmute([]u8)config_content) err := os.write_entire_file(config_path, transmute([]u8)config_content)
testing.expect(t, err == nil, "should write .git/config") testing.expect_value(t, err, nil)
remotes := get_git_remotes(base, context.temp_allocator) remotes := get_git_remotes(base, context.temp_allocator)
testing.expect(t, len(remotes) == 2, "should find 2 remotes") testing.expect_value(t, len(remotes), 2)
} }
@(test) @(test)
@@ -310,7 +310,7 @@ test_get_git_remotes_no_config :: proc(t: ^testing.T) {
remotes := get_git_remotes(base, context.temp_allocator) remotes := get_git_remotes(base, context.temp_allocator)
testing.expect(t, len(remotes) == 0, "should return empty when no .git/config") testing.expect_value(t, len(remotes), 0)
} }
@(test) @(test)
@@ -324,11 +324,11 @@ test_get_git_remotes_no_remotes :: proc(t: ^testing.T) {
config_content := "[core]\n\trepositoryformatversion = 0\n\tbare = false\n" config_content := "[core]\n\trepositoryformatversion = 0\n\tbare = false\n"
config_path := fmt.tprintf("%s/config", git_dir) config_path := fmt.tprintf("%s/config", git_dir)
err := os.write_entire_file(config_path, transmute([]u8)config_content) err := os.write_entire_file(config_path, transmute([]u8)config_content)
testing.expect(t, err == nil, "should write .git/config") testing.expect_value(t, err, nil)
remotes := get_git_remotes(base, context.temp_allocator) remotes := get_git_remotes(base, context.temp_allocator)
testing.expect(t, len(remotes) == 0, "should return empty when no remote sections") testing.expect_value(t, len(remotes), 0)
} }
@(test) @(test)
@@ -338,7 +338,7 @@ test_new_env_file :: proc(t: ^testing.T) {
env_path := fmt.tprintf("%s/.env", base) env_path := fmt.tprintf("%s/.env", base)
err := os.write_entire_file(env_path, "SECRET=value\n") err := os.write_entire_file(env_path, "SECRET=value\n")
testing.expect(t, err == nil, ".env file should exists") testing.expect_value(t, err, nil)
file, ok := new_env_file(env_path) file, ok := new_env_file(env_path)
testing.expect(t, ok, "new_env_file should succeed") testing.expect(t, ok, "new_env_file should succeed")
@@ -350,8 +350,8 @@ test_new_env_file :: proc(t: ^testing.T) {
testing.expect(t, filepath.is_abs(file.path), "path should be absolute") testing.expect(t, filepath.is_abs(file.path), "path should be absolute")
testing.expect(t, strings.has_suffix(file.path, "/.env"), "path should end with /.env") testing.expect(t, strings.has_suffix(file.path, "/.env"), "path should end with /.env")
testing.expect(t, file.contents == "SECRET=value\n", "contents mismatch") testing.expect_value(t, file.contents, "SECRET=value\n")
testing.expect(t, len(file.sha256) == 64, "sha256 should be 64 hex chars") testing.expect_value(t, len(file.sha256), 64)
} }
@(test) @(test)
@@ -366,7 +366,7 @@ test_closing_db_has_no_leaks :: proc(t: ^testing.T) {
defer os.remove_all(base) defer os.remove_all(base)
cfg_path, err := filepath.join([]string{base, "config.json"}, context.temp_allocator) cfg_path, err := filepath.join([]string{base, "config.json"}, context.temp_allocator)
testing.expect(t, err == nil, "cfgPath should build successfully") testing.expect_value(t, err, nil)
{ {
cfg := new_config([]string{"fixtures/keys/insecure-test-key"}, cfg_path) cfg := new_config([]string{"fixtures/keys/insecure-test-key"}, cfg_path)
@@ -385,7 +385,7 @@ test_open_existing_db_has_no_leaks :: proc(t: ^testing.T) {
defer os.remove_all(base) defer os.remove_all(base)
cfg_path, err := filepath.join([]string{base, "config.json"}, context.temp_allocator) cfg_path, err := filepath.join([]string{base, "config.json"}, context.temp_allocator)
testing.expect(t, err == nil, "cfgPath should build successfully") testing.expect_value(t, err, nil)
{ {
cfg := new_config([]string{"fixtures/keys/insecure-test-key"}, cfg_path) cfg := new_config([]string{"fixtures/keys/insecure-test-key"}, cfg_path)
@@ -422,7 +422,7 @@ test_db_sync_noop :: proc(t: ^testing.T) {
env_path := fmt.tprintf("%s/.env", base) env_path := fmt.tprintf("%s/.env", base)
content := "KEY=value\n" content := "KEY=value\n"
write_err := os.write_entire_file(env_path, transmute([]u8)content) write_err := os.write_entire_file(env_path, transmute([]u8)content)
testing.expect(t, write_err == nil, "should write .env file") testing.expect_value(t, write_err, nil)
digest := hash.hash_bytes( digest := hash.hash_bytes(
hash.Algorithm.SHA256, hash.Algorithm.SHA256,
@@ -441,8 +441,8 @@ test_db_sync_noop :: proc(t: ^testing.T) {
db_insert(&db, f) db_insert(&db, f)
result, sync_err := db_sync(&db, &f) result, sync_err := db_sync(&db, &f)
testing.expect(t, sync_err == .None, "sync should not error") testing.expect_value(t, sync_err, SyncError.None)
testing.expect(t, result == {}, "should be noop") testing.expect_value(t, result, nil)
} }
@(test) @(test)
@@ -453,7 +453,7 @@ test_db_sync_backed_up :: proc(t: ^testing.T) {
env_path := fmt.tprintf("%s/.env", base) env_path := fmt.tprintf("%s/.env", base)
changed_content := "KEY=changed\n" changed_content := "KEY=changed\n"
write_err := os.write_entire_file(env_path, transmute([]u8)changed_content) write_err := os.write_entire_file(env_path, transmute([]u8)changed_content)
testing.expect(t, write_err == nil, "should write .env file") testing.expect_value(t, write_err, nil)
db, ok := db_init() db, ok := db_init()
testing.expect(t, ok, "failed to create test db") testing.expect(t, ok, "failed to create test db")
@@ -464,7 +464,7 @@ test_db_sync_backed_up :: proc(t: ^testing.T) {
db_insert(&db, f) db_insert(&db, f)
result, sync_err := db_sync(&db, &f) result, sync_err := db_sync(&db, &f)
testing.expect(t, sync_err == .None, "sync should not error") testing.expect_value(t, sync_err, SyncError.None)
testing.expect(t, .BackedUp in result, "should be backed up") testing.expect(t, .BackedUp in result, "should be backed up")
} }
@@ -485,11 +485,11 @@ test_db_sync_restored :: proc(t: ^testing.T) {
db_insert(&db, f) db_insert(&db, f)
result, err := db_sync(&db, &f) result, err := db_sync(&db, &f)
testing.expect(t, err == .None, "sync should not error") testing.expect_value(t, err, SyncError.None)
testing.expect(t, .Restored in result, "should be restored") testing.expect(t, .Restored in result, "should be restored")
data, read_err := os.read_entire_file_from_path(env_path, context.temp_allocator) data, read_err := os.read_entire_file_from_path(env_path, context.temp_allocator)
testing.expect(t, read_err == nil, "file should exist after restore") testing.expect_value(t, read_err, nil)
if read_err == nil { if read_err == nil {
testing.expect_value(t, string(data), "SECRET=value") testing.expect_value(t, string(data), "SECRET=value")
} }
@@ -522,7 +522,7 @@ test_db_sync_moved :: proc(t: ^testing.T) {
config_content := "[remote \"origin\"]\n\turl = git@github.com:user/repo.git\n" config_content := "[remote \"origin\"]\n\turl = git@github.com:user/repo.git\n"
config_path := fmt.tprintf("%s/config", git_dir) config_path := fmt.tprintf("%s/config", git_dir)
write_err := os.write_entire_file(config_path, transmute([]u8)config_content) write_err := os.write_entire_file(config_path, transmute([]u8)config_content)
testing.expect(t, write_err == nil, "should write .git/config") testing.expect_value(t, write_err, nil)
db, ok := db_init() db, ok := db_init()
testing.expect(t, ok, "failed to create test db") testing.expect(t, ok, "failed to create test db")
@@ -540,7 +540,7 @@ test_db_sync_moved :: proc(t: ^testing.T) {
testing.expect(t, db_insert(&db, f), "insert should succeed") testing.expect(t, db_insert(&db, f), "insert should succeed")
result, err := db_sync(&db, &f) result, err := db_sync(&db, &f)
testing.expect(t, err == .None, "sync should not error") testing.expect_value(t, err, SyncError.None)
if err != .None do return if err != .None do return
testing.expect(t, .DirUpdated in result, "should have DirUpdated flag") testing.expect(t, .DirUpdated in result, "should have DirUpdated flag")
testing.expect(t, .Restored in result, "should have Restored flag") testing.expect(t, .Restored in result, "should have Restored flag")

View File

@@ -83,6 +83,6 @@ test_scan_path_empty_dir :: proc(t: ^testing.T) {
results, ok := scan_path(base, cfg) results, ok := scan_path(base, cfg)
defer delete(results) defer delete(results)
testing.expect(t, ok, "scan_path should succeed") testing.expect(t, ok, "scan_path should succeed")
testing.expect(t, len(results) == 0, fmt.tprintf("expected 0 results, got %d", len(results))) testing.expect_value(t, len(results), 0)
} }

View File

@@ -46,15 +46,7 @@ test_private_key_pub_matches_public_key :: proc(t: ^testing.T) {
kp, priv_ok := parse_ssh_private_key(TEST_KEY_DIR + "/test_ed25519") kp, priv_ok := parse_ssh_private_key(TEST_KEY_DIR + "/test_ed25519")
testing.expect(t, priv_ok, "expected private key to parse") testing.expect(t, priv_ok, "expected private key to parse")
testing.expect( testing.expect_value(t, pub_from_pub, kp.Public)
t,
pub_from_pub == kp.Public,
fmt.tprintf(
"public key mismatch:\n from .pub: %v\n from priv: %v",
pub_from_pub,
kp.Public,
),
)
} }
@(test) @(test)
@@ -64,12 +56,11 @@ test_read_wire_string :: proc(t: ^testing.T) {
s, ok := read_wire_string(data, &offset) s, ok := read_wire_string(data, &offset)
testing.expect(t, ok, "expected read_wire_string to succeed") testing.expect(t, ok, "expected read_wire_string to succeed")
testing.expect(t, s == "hello", fmt.tprintf("expected 'hello', got %q", s)) testing.expect_value(t, s, "hello")
testing.expect(t, offset == 9, fmt.tprintf("expected offset 9, got %d", offset)) testing.expect_value(t, offset, 9)
s2, ok2 := read_wire_string(data, &offset) s2, ok2 := read_wire_string(data, &offset)
testing.expect(t, ok2, "expected second read to succeed") testing.expect(t, ok2, "expected second read to succeed")
testing.expect(t, s2 == "", "expected empty string") testing.expect_value(t, s2, "")
} }

View File

@@ -18,21 +18,18 @@ decorations := table.Decorations {
"─", "─",
} }
ansi_aware_width :: proc(str: string) -> int { ansi_aware_width :: proc(str: string) -> int #no_bounds_check {
#no_bounds_check { width := 0
width := 0 for i := 0; i < len(str); {
i := 0 if i + 1 < len(str) && str[i] == 0x1b && str[i + 1] == '[' {
for i < len(str) { i += 2
if i + 1 < len(str) && str[i] == 0x1b && str[i + 1] == '[' { for i < len(str) {c := str[i]; i += 1; if c >= 0x40 && c <= 0x7E {break}}
i += 2 } else {
for i < len(str) {c := str[i]; i += 1; if c >= 0x40 && c <= 0x7E {break}} width += 1
} else { i += 1
width += 1
i += 1
}
} }
return width
} }
return width
} }
write_borderless_table :: proc(w: io.Writer, t: ^table.Table) { write_borderless_table :: proc(w: io.Writer, t: ^table.Table) {