diff options
author | EuAndreh <eu@euandre.org> | 2025-05-07 07:02:57 -0300 |
---|---|---|
committer | EuAndreh <eu@euandre.org> | 2025-05-07 07:16:33 -0300 |
commit | d3cb41376ac0be31ed2b6649dd8d6b42674ebeaf (patch) | |
tree | df7eeb84a91114b6624a0f0fe7c1b11fa7e5ae1f /tests/uuid.go | |
parent | tests/{cli-opts,integration}.sh: Add test implementations (diff) | |
download | uuid-d3cb41376ac0be31ed2b6649dd8d6b42674ebeaf.tar.gz uuid-d3cb41376ac0be31ed2b6649dd8d6b42674ebeaf.tar.xz |
Support UUIDv7, with -vN for choosing
Diffstat (limited to 'tests/uuid.go')
-rw-r--r-- | tests/uuid.go | 387 |
1 files changed, 371 insertions, 16 deletions
diff --git a/tests/uuid.go b/tests/uuid.go index ad9cac4..912339a 100644 --- a/tests/uuid.go +++ b/tests/uuid.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/hex" "fmt" + "io" "os" "reflect" "strings" @@ -50,18 +51,24 @@ func assertEq(given any, expected any) { } -func test_NewFrom() { - testStart("NewFrom()") + +func test_NewV4From() { + testStart("NewV4From()") + + testing("we propagate the error when it happens", func() { + _, err := NewV4From(strings.NewReader("")) + assertEq(err, io.EOF) + }) testing("we get the same UUID from the same input", func() { const s = "abcdefghijklmnop" r1 := strings.NewReader(s) - uuid1, err := NewFrom(r1) + uuid1, err := NewV4From(r1) assertEq(err, nil) r2 := strings.NewReader(s) - uuid2, err := NewFrom(r2) + uuid2, err := NewV4From(r2) assertEq(err, nil) assertEq(uuid1, uuid2) @@ -107,7 +114,7 @@ func test_NewFrom() { } r := bytes.NewReader(input) - given, err := NewFrom(r) + given, err := NewV4From(r) assertEq(err, nil) assertEq(given, expected) }) @@ -152,30 +159,166 @@ func test_NewFrom() { } r := bytes.NewReader(input) - given, err := NewFrom(r) + given, err := NewV4From(r) assertEq(err, nil) assertEq(given, expected) }) } -func test_New() { - testStart("New()") +func test_NewV4() { + testStart("NewV4()") testing("we can generate UUID values: ", func() { - var uuid UUID = New() - assertEq(len(uuid), 16) + var uuid UUID = NewV4() + assertEq(uuid == Nil, false) + assertEq(uuid == Max, false) }) testing("panic when the randomReader fails", func() { savedReader := randomReader - randomReader = strings.NewReader("abc") + randomReader = strings.NewReader("") + defer func() { + r := recover() + assertEq(r, io.EOF) + randomReader = savedReader + }() + + NewV4() + os.Exit(5) + }) +} + +func test_NewV7From() { + testStart("NewV7From()") + + testing("reader error is propagated", func() { + nowFn := func() uint64 { + return 0 + } + + _, err := NewV7From(strings.NewReader(""), nowFn) + assertEq(err, io.EOF) + }) + + testing("we get the same UUID given the same input", func() { + const s = "abcdefgh" + nowFn := func() uint64 { + return 0 + } + + r1 := strings.NewReader(s) + uuid1, err := NewV7From(r1, nowFn) + assertEq(err, nil) + + r2 := strings.NewReader(s) + uuid2, err := NewV7From(r2, nowFn) + assertEq(err, nil) + + assertEq(uuid1, uuid2) + }) + + testing("the bytes are what the reader plus the time gives", func() { + randomInput := []byte{ + 0x00, + 0x01, + 0x02, + 0x03, + 0x04, + 0x05, + 0x06, + 0x07, + } + + nowFn := func() uint64 { + return 0xffffffff00000000 + } + + expected := UUID{ + 0xff, + 0xff, + 0xff, + 0xff, + 0x00, + 0x00, + 0x00 + 0x70, + 0x00, + 0x00 + 0x80, + 0x01, + 0x02, + 0x03, + 0x04, + 0x05, + 0x06, + 0x07, + } + + r := bytes.NewReader(randomInput) + given, err := NewV7From(r, nowFn) + assertEq(err, nil) + assertEq(given, expected) + }) + + testing("v7 and variant markers", func() { + randomInput := []byte{ + 0x11, + 0x11, + 0x11, + 0x11, + 0x11, + 0x11, + 0x11, + 0x11, + } + + nowFn := func() uint64 { + return 0x1111111111111111 + } + + expected := UUID{ + 0x11, + 0x11, + 0x11, + 0x11, + 0x11, + 0x11, + 0x71, // not 0x11 + 0x11, + 0x91, // not 0x11 + 0x11, + 0x11, + 0x11, + 0x11, + 0x11, + 0x11, + 0x11, + } + + r := bytes.NewReader(randomInput) + given, err := NewV7From(r, nowFn) + assertEq(err, nil) + assertEq(given, expected) + }) +} + +func test_NewV7() { + testStart("NewV7()") + + testing("we can generate UUID values: ", func() { + var uuid UUID = NewV7() + assertEq(uuid == Nil, false) + assertEq(uuid == Max, false) + }) + + testing("panic when reader fails", func() { + savedReader := randomReader + randomReader = strings.NewReader("") defer func() { r := recover() - assertEq(r == nil, false) + assertEq(r, io.EOF) randomReader = savedReader }() - New() + NewV7() os.Exit(5) }) } @@ -224,7 +367,7 @@ func test_FromString() { testing("UUID -> string -> UUID round trip", func() { for i := 0; i < 100; i++ { - uuid0 := New() + uuid0 := NewV4() uuid1, err := FromString(uuid0.String()) assertEq(err, nil) assertEq(uuid0, uuid1) @@ -251,11 +394,223 @@ func test_FromString() { }) } +func test_usage() { + testStart("usage()") + + testing("all it does is write to the given io.Writer", func() { + w := strings.Builder{} + usage("xxx", &w) + + const expectedRaw = ` + Usage: + xxx [-v (4|7)] + xxx STRING + ` + expected := strings.Trim( + strings.ReplaceAll(expectedRaw, "\t", ""), + "\n", + ) + "\n" + assertEq(w.String(), expected) + }) +} + +func test_actionForSubargs() { + testStart("actionForSubargs()") + + testing("we decide based only on length", func() { + assertEq(actionForSubargs([]string{}), actionType_generate) + assertEq(actionForSubargs([]string{""}), actionType_parse) + assertEq(actionForSubargs([]string{"id"}), actionType_parse) + }) +} + +func test_getopt() { + testStart("getopt()") + + const usageRaw = ` + Usage: + $0 [-v (4|7)] + $0 STRING + ` + usage := strings.Trim( + strings.ReplaceAll(usageRaw, "\t", ""), + "\n", + ) + "\n" + + testing("we supress the default error message", func() { + w := strings.Builder{} + argv := []string{"$0", "-h"} + _, rc := getopt(argv, &w) + + assertEq(w.String(), usage) + assertEq(rc, 2) + }) + + testing("we get unsupported flag error", func() { + w := strings.Builder{} + argv := []string{"$0", "-Z"} + _, rc := getopt(argv, &w) + + const message = "flag provided but not defined: -Z\n" + assertEq(w.String(), message + usage) + assertEq(rc, 2) + }) + + testing("we get incorrect use of flag error", func() { + w := strings.Builder{} + argv := []string{"$0", "-v"} + _, rc := getopt(argv, &w) + + const message = "flag needs an argument: -v\n" + assertEq(w.String(), message + usage) + assertEq(rc, 2) + }) + + testing("we get bad flag value error", func() { + w := strings.Builder{} + argv := []string{"$0", "-v", "a"} + _, rc := getopt(argv, &w) + + const message = "invalid value \"a\" for flag -v: parse error\n" + assertEq(w.String(), message + usage) + assertEq(rc, 2) + }) + + testing("the args has the picked version", func() { + var ( + w1 = strings.Builder{} + w2 = strings.Builder{} + w3 = strings.Builder{} + ) + + argsIn1 := []string{"$0"} + argsIn2 := []string{"$0", "-v", "4"} + argsIn3 := []string{"$0", "-v", "7"} + + args1, rc1 := getopt(argsIn1, &w1) + args2, rc2 := getopt(argsIn2, &w2) + args3, rc3 := getopt(argsIn3, &w3) + + expected1 := argsT{ + allArgs: []string{"$0"}, + subArgs: []string{}, + action: actionType_generate, + version: 4, + } + expected2 := argsT{ + allArgs: []string{"$0", "-v", "4"}, + subArgs: []string{}, + action: actionType_generate, + version: 4, + } + expected3 := argsT{ + allArgs: []string{"$0", "-v", "7"}, + subArgs: []string{}, + action: actionType_generate, + version: 7, + } + + assertEq(w1.String(), "") + assertEq(w2.String(), "") + assertEq(w3.String(), "") + assertEq(rc1, 0) + assertEq(rc2, 0) + assertEq(rc3, 0) + assertEq(args1, expected1) + assertEq(args2, expected2) + assertEq(args3, expected3) + }) + + testing("the args has the picked action", func() { + var ( + w1 = strings.Builder{} + w2 = strings.Builder{} + ) + + argsIn1 := []string{"$0", "the-string"} + argsIn2 := []string{"$0", "-v", "7", "the-string"} + + args1, rc1 := getopt(argsIn1, &w1) + args2, rc2 := getopt(argsIn2, &w2) + + expected1 := argsT{ + allArgs: []string{"$0", "the-string"}, + subArgs: []string{"the-string"}, + action: actionType_parse, + version: 4, + } + expected2 := argsT{ + allArgs: []string{"$0", "-v", "7", "the-string"}, + subArgs: []string{"the-string"}, + action: actionType_parse, + version: 7, + } + + assertEq(w1.String(), "") + assertEq(w2.String(), "") + assertEq(rc1, 0) + assertEq(rc2, 0) + assertEq(args1, expected1) + assertEq(args2, expected2) + }) +} + +func test_run() { + testStart("run()") + + testing("generating IDs gives us 0 rc", func() { + out1 := strings.Builder{} + out2 := strings.Builder{} + args1 := argsT{ + action: actionType_generate, + version: 4, + } + args2 := argsT{ + action: actionType_generate, + version: 7, + } + + rc1 := run(args1, nil, &out1, nil) + rc2 := run(args2, nil, &out2, nil) + + assertEq(rc1, 0) + assertEq(rc2, 0) + assertEq(len(out1.String()), encodedLength + len("\n")) + assertEq(len(out2.String()), encodedLength + len("\n")) + }) + + testing("parsing varies based on ID validity", func() { + const id = "ggggggg-ggggg-gggg-gggg-gggggggggggg" + err1 := strings.Builder{} + args1 := argsT{ + action: actionType_parse, + subArgs: []string{ id }, + } + args2 := argsT{ + action: actionType_parse, + subArgs: []string{New().String()}, + } + + rc1 := run(args1, nil, nil, &err1) + rc2 := run(args2, nil, nil, nil) + + assertEq(rc1, 3) + assertEq(rc2, 0) + assertEq(err1.String(), "uuid: Bad char in string\n") + }) +} + func MainTest() { - test_NewFrom() - test_New() + test_NewV4From() + test_NewV4() + test_NewV7From() + test_NewV7() test_String() test_FromString() + test_usage() + test_actionForSubargs() + test_getopt() + test_run() } |