mocks_test.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. package tools
  2. import (
  3. "context"
  4. "fmt"
  5. "sort"
  6. "strconv"
  7. "strings"
  8. "time"
  9. "github.com/google/uuid"
  10. "github.com/kujtimiihoxha/termai/internal/history"
  11. "github.com/kujtimiihoxha/termai/internal/permission"
  12. "github.com/kujtimiihoxha/termai/internal/pubsub"
  13. )
  14. // Mock permission service for testing
  15. type mockPermissionService struct {
  16. *pubsub.Broker[permission.PermissionRequest]
  17. allow bool
  18. }
  19. func (m *mockPermissionService) GrantPersistant(permission permission.PermissionRequest) {
  20. // Not needed for tests
  21. }
  22. func (m *mockPermissionService) Grant(permission permission.PermissionRequest) {
  23. // Not needed for tests
  24. }
  25. func (m *mockPermissionService) Deny(permission permission.PermissionRequest) {
  26. // Not needed for tests
  27. }
  28. func (m *mockPermissionService) Request(opts permission.CreatePermissionRequest) bool {
  29. return m.allow
  30. }
  31. func newMockPermissionService(allow bool) permission.Service {
  32. return &mockPermissionService{
  33. Broker: pubsub.NewBroker[permission.PermissionRequest](),
  34. allow: allow,
  35. }
  36. }
  37. type mockFileHistoryService struct {
  38. *pubsub.Broker[history.File]
  39. files map[string]history.File // ID -> File
  40. timeNow func() int64
  41. }
  42. // Create implements history.Service.
  43. func (m *mockFileHistoryService) Create(ctx context.Context, sessionID string, path string, content string) (history.File, error) {
  44. return m.createWithVersion(ctx, sessionID, path, content, history.InitialVersion)
  45. }
  46. // CreateVersion implements history.Service.
  47. func (m *mockFileHistoryService) CreateVersion(ctx context.Context, sessionID string, path string, content string) (history.File, error) {
  48. var files []history.File
  49. for _, file := range m.files {
  50. if file.Path == path {
  51. files = append(files, file)
  52. }
  53. }
  54. if len(files) == 0 {
  55. // No previous versions, create initial
  56. return m.Create(ctx, sessionID, path, content)
  57. }
  58. // Sort files by CreatedAt in descending order
  59. sort.Slice(files, func(i, j int) bool {
  60. return files[i].CreatedAt > files[j].CreatedAt
  61. })
  62. // Get the latest version
  63. latestFile := files[0]
  64. latestVersion := latestFile.Version
  65. // Generate the next version
  66. var nextVersion string
  67. if latestVersion == history.InitialVersion {
  68. nextVersion = "v1"
  69. } else if strings.HasPrefix(latestVersion, "v") {
  70. versionNum, err := strconv.Atoi(latestVersion[1:])
  71. if err != nil {
  72. // If we can't parse the version, just use a timestamp-based version
  73. nextVersion = fmt.Sprintf("v%d", latestFile.CreatedAt)
  74. } else {
  75. nextVersion = fmt.Sprintf("v%d", versionNum+1)
  76. }
  77. } else {
  78. // If the version format is unexpected, use a timestamp-based version
  79. nextVersion = fmt.Sprintf("v%d", latestFile.CreatedAt)
  80. }
  81. return m.createWithVersion(ctx, sessionID, path, content, nextVersion)
  82. }
  83. func (m *mockFileHistoryService) createWithVersion(_ context.Context, sessionID, path, content, version string) (history.File, error) {
  84. now := m.timeNow()
  85. file := history.File{
  86. ID: uuid.New().String(),
  87. SessionID: sessionID,
  88. Path: path,
  89. Content: content,
  90. Version: version,
  91. CreatedAt: now,
  92. UpdatedAt: now,
  93. }
  94. m.files[file.ID] = file
  95. m.Publish(pubsub.CreatedEvent, file)
  96. return file, nil
  97. }
  98. // Delete implements history.Service.
  99. func (m *mockFileHistoryService) Delete(ctx context.Context, id string) error {
  100. file, ok := m.files[id]
  101. if !ok {
  102. return fmt.Errorf("file not found: %s", id)
  103. }
  104. delete(m.files, id)
  105. m.Publish(pubsub.DeletedEvent, file)
  106. return nil
  107. }
  108. // DeleteSessionFiles implements history.Service.
  109. func (m *mockFileHistoryService) DeleteSessionFiles(ctx context.Context, sessionID string) error {
  110. files, err := m.ListBySession(ctx, sessionID)
  111. if err != nil {
  112. return err
  113. }
  114. for _, file := range files {
  115. err = m.Delete(ctx, file.ID)
  116. if err != nil {
  117. return err
  118. }
  119. }
  120. return nil
  121. }
  122. // Get implements history.Service.
  123. func (m *mockFileHistoryService) Get(ctx context.Context, id string) (history.File, error) {
  124. file, ok := m.files[id]
  125. if !ok {
  126. return history.File{}, fmt.Errorf("file not found: %s", id)
  127. }
  128. return file, nil
  129. }
  130. // GetByPathAndSession implements history.Service.
  131. func (m *mockFileHistoryService) GetByPathAndSession(ctx context.Context, path string, sessionID string) (history.File, error) {
  132. var latestFile history.File
  133. var found bool
  134. var latestTime int64
  135. for _, file := range m.files {
  136. if file.Path == path && file.SessionID == sessionID {
  137. if !found || file.CreatedAt > latestTime {
  138. latestFile = file
  139. latestTime = file.CreatedAt
  140. found = true
  141. }
  142. }
  143. }
  144. if !found {
  145. return history.File{}, fmt.Errorf("file not found: %s for session %s", path, sessionID)
  146. }
  147. return latestFile, nil
  148. }
  149. // ListBySession implements history.Service.
  150. func (m *mockFileHistoryService) ListBySession(ctx context.Context, sessionID string) ([]history.File, error) {
  151. var files []history.File
  152. for _, file := range m.files {
  153. if file.SessionID == sessionID {
  154. files = append(files, file)
  155. }
  156. }
  157. // Sort by CreatedAt in descending order
  158. sort.Slice(files, func(i, j int) bool {
  159. return files[i].CreatedAt > files[j].CreatedAt
  160. })
  161. return files, nil
  162. }
  163. // ListLatestSessionFiles implements history.Service.
  164. func (m *mockFileHistoryService) ListLatestSessionFiles(ctx context.Context, sessionID string) ([]history.File, error) {
  165. // Map to track the latest file for each path
  166. latestFiles := make(map[string]history.File)
  167. for _, file := range m.files {
  168. if file.SessionID == sessionID {
  169. existing, ok := latestFiles[file.Path]
  170. if !ok || file.CreatedAt > existing.CreatedAt {
  171. latestFiles[file.Path] = file
  172. }
  173. }
  174. }
  175. // Convert map to slice
  176. var result []history.File
  177. for _, file := range latestFiles {
  178. result = append(result, file)
  179. }
  180. // Sort by CreatedAt in descending order
  181. sort.Slice(result, func(i, j int) bool {
  182. return result[i].CreatedAt > result[j].CreatedAt
  183. })
  184. return result, nil
  185. }
  186. // Subscribe implements history.Service.
  187. func (m *mockFileHistoryService) Subscribe(ctx context.Context) <-chan pubsub.Event[history.File] {
  188. return m.Broker.Subscribe(ctx)
  189. }
  190. // Update implements history.Service.
  191. func (m *mockFileHistoryService) Update(ctx context.Context, file history.File) (history.File, error) {
  192. _, ok := m.files[file.ID]
  193. if !ok {
  194. return history.File{}, fmt.Errorf("file not found: %s", file.ID)
  195. }
  196. file.UpdatedAt = m.timeNow()
  197. m.files[file.ID] = file
  198. m.Publish(pubsub.UpdatedEvent, file)
  199. return file, nil
  200. }
  201. func newMockFileHistoryService() history.Service {
  202. return &mockFileHistoryService{
  203. Broker: pubsub.NewBroker[history.File](),
  204. files: make(map[string]history.File),
  205. timeNow: func() int64 { return time.Now().Unix() },
  206. }
  207. }