180 lines
5.1 KiB
Go

package storage
import (
"context"
"fmt"
"strings"
"sync"
"time"
service "gadrid/internal/service/storage" // Adjust this import path
)
// InMemStorageMock is a mock implementation of PGPKeyStorage for testing purposes.
// It stores data in memory, mimicking database operations.
type InMemStorageMock struct {
keys map[string]*service.PGPkey
uids map[string]map[string]*service.PGPUid // fingerprint -> uid_string -> UID
mu sync.RWMutex // Mutex for concurrent access safety
}
// NewInMemStorageMock creates a new in-memory mock storage.
func NewInMemStorageMock() *InMemStorageMock {
return &InMemStorageMock{
keys: make(map[string]*service.PGPkey),
uids: make(map[string]map[string]*service.PGPUid),
}
}
// AddKey implements PGPKeyStorage interface for the mock.
// See PGPKeyStorage interface for documentation.
func (m *InMemStorageMock) AddKey(ctx context.Context, keys []*service.PGPkey) error {
m.mu.Lock()
defer m.mu.Unlock()
for _, k := range keys {
// Simulate UPSERT for pgp_keys
m.keys[k.Fingerprint] = &service.PGPkey{ // Deep copy
Fingerprint: k.Fingerprint,
Packet: k.Packet,
Revoked: k.Revoked,
// Uids are handled separately, so this slice will be empty here in the key itself
}
// Ensure the inner map exists for UIDs
if _, ok := m.uids[k.Fingerprint]; !ok {
m.uids[k.Fingerprint] = make(map[string]*service.PGPUid)
}
// Simulate INSERT (ON CONFLICT DO NOTHING) for pgp_uids
for _, u := range k.Uids {
if _, exists := m.uids[k.Fingerprint][u.UIDString]; !exists {
m.uids[k.Fingerprint][u.UIDString] = &service.PGPUid{ // Deep copy
UIDString: u.UIDString,
Email: u.Email,
Verify: u.Verify,
Token: u.Token,
TokenExpires: u.TokenExpires,
}
}
}
}
return nil
}
// VerifyUID implements PGPKeyStorage interface for the mock.
// See PGPKeyStorage interface for documentation.
func (m *InMemStorageMock) VerifyUID(ctx context.Context, fingerprint, email, token string) error {
m.mu.Lock()
defer m.mu.Unlock()
keyUids, ok := m.uids[fingerprint]
if !ok {
return fmt.Errorf("invalid verification token or link") // Mimic no matching entry
}
var targetUID *service.PGPUid
var targetUIDString string // To be used as key in the map for update
for uidStr, uid := range keyUids {
if uid.Email == email {
targetUID = uid
targetUIDString = uidStr
break
}
}
if targetUID == nil {
return fmt.Errorf("invalid verification token or link") // Mimic no matching entry by email
}
if targetUID.Verify {
return fmt.Errorf("UID for %s is already verified", email)
}
if targetUID.Token == "" || targetUID.Token != token {
return fmt.Errorf("invalid verification token or link")
}
if targetUID.TokenExpires.Before(time.Now()) {
return fmt.Errorf("verification token has expired")
}
// Simulate update
targetUID.Verify = true
targetUID.Token = "" // Simulate setting to null
targetUID.TokenExpires = time.Time{} // Simulate setting to null
// Ensure the updated UID is correctly stored back in the map
m.uids[fingerprint][targetUIDString] = targetUID
return nil
}
// GetKey implements PGPKeyStorage interface for the mock.
// See PGPKeyStorage interface for documentation.
func (m *InMemStorageMock) GetKey(ctx context.Context, fingerprint string) ([]*service.PGPkey, error) {
m.mu.RLock()
defer m.mu.RUnlock()
pgpKey, ok := m.keys[fingerprint]
if !ok {
return nil, ErrKeyNotFound // Use the specific error defined in the interface package
}
// Create a deep copy of the key
resultKey := &service.PGPkey{
Fingerprint: pgpKey.Fingerprint,
Packet: pgpKey.Packet,
Revoked: pgpKey.Revoked,
Uids: []*service.PGPUid{}, // Only add verified UIDs
}
// Filter UIDs by 'verified = true'
if keyUids, ok := m.uids[fingerprint]; ok {
for _, uid := range keyUids {
if uid.Verify {
// Deep copy the UID
resultKey.Uids = append(resultKey.Uids, &service.PGPUid{
UIDString: uid.UIDString,
Email: uid.Email,
Verify: uid.Verify,
Token: uid.Token,
TokenExpires: uid.TokenExpires,
})
}
}
}
return []*service.PGPkey{resultKey}, nil
}
// Index implements PGPKeyStorage interface for the mock.
// See PGPKeyStorage interface for documentation.
func (m *InMemStorageMock) Index(ctx context.Context, q string) ([]*service.PGPkey, error) {
m.mu.RLock()
defer m.mu.RUnlock()
matchedFingerprints := make(map[string]bool)
lowerQ := strings.ToLower(q) // Simulate case-insensitive search
for f, uidMap := range m.uids {
for _, uid := range uidMap {
// Simulate case-insensitive substring match for regex.
// For a more accurate mock, you'd use the 'regexp' package here.
if strings.Contains(strings.ToLower(uid.UIDString), lowerQ) ||
strings.Contains(strings.ToLower(uid.Email), lowerQ) {
matchedFingerprints[f] = true
}
}
}
var resultKeys []*service.PGPkey
for f := range matchedFingerprints {
// Reuse GetKey logic from mock (which filters for verified UIDs)
keys, err := m.GetKey(ctx, f)
if err == nil && len(keys) > 0 { // Check error from GetKey (e.g., ErrKeyNotFound)
resultKeys = append(resultKeys, keys[0])
}
}
return resultKeys, nil
}