1
0

gonanoid_test.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. package gonanoid
  2. import (
  3. "math"
  4. "strings"
  5. "testing"
  6. )
  7. var urlLength = len(defaults.Alphabet)
  8. // Test that nanoid generates URL friendly IDs
  9. // it ('generates URL-friendly IDs')
  10. func TestGeneratesURLFriendlyIDs(t *testing.T) {
  11. for i := 0; i < 10; i++ {
  12. id, err := Nanoid()
  13. if err != nil {
  14. t.Errorf("Nanoid error: %v", err)
  15. }
  16. if len(id) != defaults.Size {
  17. t.Errorf(
  18. "TestGeneratesURLFriendlyIDs error: length of id %v should be %v, got %v",
  19. id,
  20. defaults.Size,
  21. id,
  22. )
  23. }
  24. runeID := []rune(id)
  25. for j := 0; j < len(runeID); j++ {
  26. res := strings.Contains(defaults.Alphabet, string(runeID[j]))
  27. if !res {
  28. t.Errorf(
  29. "GeneratesURLFriendlyIds error: char %v should be contained in %v",
  30. string(runeID[j]),
  31. defaults.Alphabet,
  32. )
  33. }
  34. }
  35. }
  36. }
  37. // Test that nanoid has no collisions
  38. // it ('has no collisions')
  39. func TestHasNoCollisions(t *testing.T) {
  40. COUNT := 100 * 1000
  41. used := make(map[string]bool)
  42. for i := 0; i < COUNT; i++ {
  43. id, err := Nanoid()
  44. if err != nil {
  45. t.Errorf("Nanoid error: %v", err)
  46. }
  47. if used[id] {
  48. t.Errorf("Collision error! Id %v found for test arr %v", id, used)
  49. }
  50. used[id] = true
  51. }
  52. }
  53. // Test that Nanoid has flat distribution
  54. // it ('has flat distribution')
  55. func TestFlatDistribution(t *testing.T) {
  56. COUNT := 100 * 1000
  57. instance, err := Nanoid()
  58. if err != nil {
  59. t.Errorf("Nanoid error: %v", err)
  60. }
  61. LENGTH := len(instance)
  62. chars := make(map[byte]int)
  63. for i := 0; i < COUNT; i++ {
  64. id, _ := Nanoid()
  65. for j := 0; j < LENGTH; j++ {
  66. // https://github.com/ai/nanoid/blob/d6ad3412147fa4c2b0d404841ade245a00c2009f/test/index.test.js#L33
  67. // if (!chars[char]) chars[char] = 0 is useless since it
  68. // is initialized by default to 0 from Golang
  69. chars[id[j]]++
  70. }
  71. }
  72. for char, k := range chars {
  73. distribution := float64(k) * float64(urlLength) / float64(COUNT*LENGTH)
  74. if !toBeCloseTo(distribution, 1, 1) {
  75. t.Errorf("Distribution error! Distribution %v found for char %v", distribution, char)
  76. }
  77. }
  78. }
  79. // utility that replicates jest.toBeCloseTo
  80. func toBeCloseTo(value, actual, expected float64) bool {
  81. precision := 2
  82. // https://github.com/facebook/jest/blob/a397abaf9f08e691f8739899819fc4da41c1e476/packages/expect/src/matchers.js#L83
  83. pass := math.Abs(expected-actual) < math.Pow10(-precision)/2
  84. return pass
  85. }
  86. // Benchmark nanoid generator
  87. func BenchmarkNanoid(b *testing.B) {
  88. for n := 0; n < b.N; n++ {
  89. Nanoid()
  90. }
  91. }