store_test.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. package auth
  2. import (
  3. "context"
  4. "strings"
  5. "testing"
  6. "time"
  7. )
  8. // fakeStore 是 Store 的内存实现,供中间件与业务逻辑的单测使用。
  9. type fakeStore struct {
  10. sessions map[string]Session // token_hash -> session
  11. tokens map[string]APIToken
  12. users map[string]User
  13. touchedSessions []string
  14. touchedTokens []string
  15. deletedSessions []string
  16. }
  17. func newFakeStore() *fakeStore {
  18. return &fakeStore{
  19. sessions: make(map[string]Session),
  20. tokens: make(map[string]APIToken),
  21. users: make(map[string]User),
  22. }
  23. }
  24. func (f *fakeStore) FindSessionByHash(_ context.Context, tokenHash string) (Session, error) {
  25. s, ok := f.sessions[tokenHash]
  26. if !ok {
  27. return Session{}, ErrNotFound
  28. }
  29. return s, nil
  30. }
  31. func (f *fakeStore) TouchSession(_ context.Context, id string, _ time.Time) error {
  32. f.touchedSessions = append(f.touchedSessions, id)
  33. return nil
  34. }
  35. func (f *fakeStore) DeleteSession(_ context.Context, id string) error {
  36. f.deletedSessions = append(f.deletedSessions, id)
  37. return nil
  38. }
  39. func (f *fakeStore) FindTokenByHash(_ context.Context, tokenHash string) (APIToken, error) {
  40. t, ok := f.tokens[tokenHash]
  41. if !ok {
  42. return APIToken{}, ErrNotFound
  43. }
  44. return t, nil
  45. }
  46. func (f *fakeStore) TouchToken(_ context.Context, id string, _ time.Time) error {
  47. f.touchedTokens = append(f.touchedTokens, id)
  48. return nil
  49. }
  50. func (f *fakeStore) GetUser(_ context.Context, id string) (User, error) {
  51. u, ok := f.users[id]
  52. if !ok {
  53. return User{}, ErrNotFound
  54. }
  55. return u, nil
  56. }
  57. var _ Store = (*fakeStore)(nil)
  58. // HashTokenForTest 暴露「剥离会话标记后再哈希」的行为,供中间件测试构造 key。
  59. func HashTokenForTest(plain string) string {
  60. base, _ := splitSessionToken(plain)
  61. return base
  62. }
  63. func TestHashTokenConsistency(t *testing.T) {
  64. // 相同输入必须得到相同哈希,用于按 token_hash 查库。
  65. if HashToken("abc") != HashToken("abc") {
  66. t.Error("同一输入的哈希应一致")
  67. }
  68. }
  69. func TestSplitPendingTokenEdgeCases(t *testing.T) {
  70. cases := []struct {
  71. in string
  72. token string
  73. trust bool
  74. }{
  75. {in: "haven_pend_xyz", token: "haven_pend_xyz", trust: false},
  76. {in: "haven_pend_xyz:1", token: "haven_pend_xyz", trust: true},
  77. {in: "haven_pend_xyz:0", token: "haven_pend_xyz:0", trust: false},
  78. {in: ":1", token: "", trust: true},
  79. {in: "", token: "", trust: false},
  80. }
  81. for _, tc := range cases {
  82. tok, trust := splitPendingToken(tc.in)
  83. if tok != tc.token || trust != tc.trust {
  84. t.Errorf("splitPendingToken(%q) = (%q, %v),期望 (%q, %v)",
  85. tc.in, tok, trust, tc.token, tc.trust)
  86. }
  87. }
  88. }
  89. func TestIdentityString(t *testing.T) {
  90. if got := Anonymous.String(); got != "anonymous" {
  91. t.Errorf("Anonymous.String() = %q", got)
  92. }
  93. if got := SessionUser.String(); got != "session" {
  94. t.Errorf("SessionUser.String() = %q", got)
  95. }
  96. if got := TokenUser.String(); got != "token" {
  97. t.Errorf("TokenUser.String() = %q", got)
  98. }
  99. }
  100. func TestIdentityIsLoggedIn(t *testing.T) {
  101. if (Identity{Kind: Anonymous}).IsLoggedIn() {
  102. t.Error("Anonymous 不应算已登录")
  103. }
  104. if !(Identity{Kind: SessionUser}).IsLoggedIn() {
  105. t.Error("SessionUser 应算已登录")
  106. }
  107. if !(Identity{Kind: TokenUser}).IsLoggedIn() {
  108. t.Error("TokenUser 应算已登录")
  109. }
  110. }
  111. func TestTruncatePrefix(t *testing.T) {
  112. if got := truncatePrefix("abc"); got != "abc" {
  113. t.Errorf("短于前缀长度的应原样返回,实际 %q", got)
  114. }
  115. if got := truncatePrefix("haven_ro_abcdefghij"); got != "haven_ro" {
  116. t.Errorf("应截取前 8 位,实际 %q", got)
  117. }
  118. if strings.HasPrefix(truncatePrefix("haven_ro_abcdefghij"), "haven_ro_") {
  119. t.Error("前 8 位不应包含 scope 后的随机串")
  120. }
  121. }
苏ICP备2026082406号
苏公网安备 待审核