walinator_test.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. package metric
  2. import (
  3. "reflect"
  4. "testing"
  5. "time"
  6. "github.com/opencost/opencost/core/pkg/exporter"
  7. "github.com/opencost/opencost/core/pkg/storage"
  8. "github.com/opencost/opencost/core/pkg/util/timeutil"
  9. "github.com/opencost/opencost/modules/collector-source/pkg/metric/aggregator"
  10. "github.com/opencost/opencost/modules/collector-source/pkg/util"
  11. )
  12. const TestActiveMinutesID = "TestActiveMinutes"
  13. const TestAverageID = "TestAverage"
  14. const TestMetric = "test_metric"
  15. func testMetricCollector() MetricStore {
  16. memStore := NewInMemoryMetricStore()
  17. memStore.Register(NewMetricCollector(
  18. TestActiveMinutesID,
  19. TestMetric,
  20. []string{
  21. "test",
  22. },
  23. aggregator.Uptime,
  24. nil,
  25. ))
  26. memStore.Register(NewMetricCollector(
  27. TestAverageID,
  28. TestMetric,
  29. []string{
  30. "test",
  31. },
  32. aggregator.AverageOverTime,
  33. nil,
  34. ))
  35. return memStore
  36. }
  37. func TestWalinator_Update(t *testing.T) {
  38. time2 := time.Now().UTC().Truncate(timeutil.Day)
  39. time1 := time2.Add(-timeutil.Day)
  40. store := storage.NewMemoryStorage()
  41. res1d, _ := util.NewResolution(util.ResolutionConfiguration{
  42. Interval: "1d",
  43. Retention: 3,
  44. })
  45. resolutions := []*util.Resolution{
  46. res1d,
  47. }
  48. repo := NewMetricRepository(
  49. resolutions,
  50. testMetricCollector,
  51. )
  52. wal, _ := NewWalinator(
  53. "test",
  54. "test",
  55. store,
  56. resolutions,
  57. repo,
  58. )
  59. inputUpdates1 := []Update{
  60. {
  61. Name: TestMetric,
  62. Labels: map[string]string{
  63. "test": "test",
  64. },
  65. Value: 1,
  66. AdditionalInfo: nil,
  67. },
  68. }
  69. wal.Update(&UpdateSet{
  70. Timestamp: time1,
  71. Updates: inputUpdates1,
  72. })
  73. // check that the repo has a collector
  74. if len(repo.resolutionStores["1d"].collectors) != 1 {
  75. t.Error("call to Update did not update repository correctly")
  76. }
  77. files, _ := store.List(wal.paths.Dir())
  78. // check storage
  79. if len(files) != 1 {
  80. t.Error("Update did not update storage")
  81. }
  82. }
  83. func TestWalinator_restore(t *testing.T) {
  84. time3 := time.Now().UTC().Truncate(timeutil.Day)
  85. time2 := time3.Add(-12 * time.Hour)
  86. time1 := time3.Add(-timeutil.Day)
  87. store := storage.NewMemoryStorage()
  88. res1d, _ := util.NewResolution(util.ResolutionConfiguration{
  89. Interval: "1d",
  90. Retention: 3,
  91. })
  92. resolutions := []*util.Resolution{
  93. res1d,
  94. }
  95. repo := NewMetricRepository(
  96. resolutions,
  97. testMetricCollector,
  98. )
  99. wal, _ := NewWalinator(
  100. "test",
  101. "test",
  102. store,
  103. resolutions,
  104. repo,
  105. )
  106. inputUpdates1 := []Update{
  107. {
  108. Name: TestMetric,
  109. Labels: map[string]string{
  110. "test": "test",
  111. },
  112. Value: 1,
  113. AdditionalInfo: nil,
  114. },
  115. }
  116. inputUpdates2 := []Update{
  117. {
  118. Name: TestMetric,
  119. Labels: map[string]string{
  120. "test": "test",
  121. },
  122. Value: 2,
  123. AdditionalInfo: nil,
  124. },
  125. }
  126. inputUpdates3 := []Update{
  127. {
  128. Name: TestMetric,
  129. Labels: map[string]string{
  130. "test": "test",
  131. },
  132. Value: 3,
  133. AdditionalInfo: nil,
  134. },
  135. }
  136. wal.Update(&UpdateSet{
  137. Timestamp: time1,
  138. Updates: inputUpdates1,
  139. })
  140. wal.Update(&UpdateSet{
  141. Timestamp: time2,
  142. Updates: inputUpdates2,
  143. })
  144. wal.Update(&UpdateSet{
  145. Timestamp: time3,
  146. Updates: inputUpdates3,
  147. })
  148. repo2 := NewMetricRepository(
  149. resolutions,
  150. testMetricCollector,
  151. )
  152. // replace the repo in the walinator
  153. wal.updater = repo2
  154. wal.restore()
  155. collector1, err := repo.GetCollector("1d", time3)
  156. if err != nil {
  157. t.Fatalf("failed to get collector from repo1: %s", err.Error())
  158. }
  159. activeMinutesRes1, err := collector1.Query(TestActiveMinutesID)
  160. if err != nil {
  161. t.Fatalf("failed to query %s from repo1: %s", TestActiveMinutesID, err.Error())
  162. }
  163. averageRes1, err := collector1.Query(TestAverageID)
  164. if err != nil {
  165. t.Fatalf("failed to query %s from repo1: %s", TestAverageID, err.Error())
  166. }
  167. collector2, err := repo2.GetCollector("1d", time3)
  168. if err != nil {
  169. t.Fatalf("failed to get collector from repo2: %s", err.Error())
  170. }
  171. activeMinutesRes2, err := collector2.Query(TestActiveMinutesID)
  172. if err != nil {
  173. t.Fatalf("failed to query %s from repo2: %s", TestActiveMinutesID, err.Error())
  174. }
  175. averageRes2, err := collector2.Query(TestAverageID)
  176. if err != nil {
  177. t.Fatalf("failed to query %s from repo2: %s", TestAverageID, err.Error())
  178. }
  179. if !reflect.DeepEqual(activeMinutesRes1, activeMinutesRes2) {
  180. t.Errorf("active minute query results did not match 1: %v, 2: %v", activeMinutesRes1, activeMinutesRes2)
  181. }
  182. if !reflect.DeepEqual(averageRes1, averageRes2) {
  183. t.Errorf("average query results did not match 1: %v, 2: %v", averageRes1, averageRes2)
  184. }
  185. }
  186. func TestWalinator_clean(t *testing.T) {
  187. time3 := time.Now().UTC().Truncate(timeutil.Day)
  188. time2 := time3.Add(-timeutil.Day)
  189. time1 := time2.Add(-timeutil.Day)
  190. store := storage.NewMemoryStorage()
  191. res1d, _ := util.NewResolution(util.ResolutionConfiguration{
  192. Interval: "1d",
  193. Retention: 2,
  194. })
  195. resolutions := []*util.Resolution{
  196. res1d,
  197. }
  198. repo := NewMetricRepository(
  199. resolutions,
  200. testMetricCollector,
  201. )
  202. wal, _ := NewWalinator(
  203. "test",
  204. "test",
  205. store,
  206. resolutions,
  207. repo,
  208. )
  209. inputUpdates1 := []Update{
  210. {
  211. Name: TestMetric,
  212. Labels: map[string]string{
  213. "test": "test",
  214. },
  215. Value: 1,
  216. AdditionalInfo: nil,
  217. },
  218. }
  219. wal.Update(&UpdateSet{
  220. Timestamp: time1,
  221. Updates: inputUpdates1,
  222. })
  223. wal.Update(&UpdateSet{
  224. Timestamp: time2,
  225. Updates: inputUpdates1,
  226. })
  227. wal.Update(&UpdateSet{
  228. Timestamp: time3,
  229. Updates: inputUpdates1,
  230. })
  231. files, err := wal.getFileInfos()
  232. if err != nil {
  233. t.Errorf("failed to retrieve file info: %s", err.Error())
  234. }
  235. if len(files) != 3 {
  236. t.Errorf("incorrect number of files after updates: wanted %d, got %d", 3, len(files))
  237. }
  238. wal.clean()
  239. files, err = wal.getFileInfos()
  240. if err != nil {
  241. t.Errorf("failed to retrieve file info: %s", err.Error())
  242. }
  243. if len(files) != 2 {
  244. t.Errorf("incorrect number of files after clean: wanted %d, got %d", 2, len(files))
  245. }
  246. }
  247. func Test_deserializeUpdateSet(t *testing.T) {
  248. inputUpdateSet1 := &UpdateSet{
  249. Updates: []Update{
  250. {
  251. Name: TestMetric,
  252. Labels: map[string]string{
  253. "test": "test",
  254. },
  255. Value: 1,
  256. AdditionalInfo: nil,
  257. },
  258. },
  259. }
  260. jsonEncoder := exporter.NewJSONEncoder[UpdateSet]()
  261. gZipJsonEncoder := exporter.NewGZipEncoder(exporter.NewJSONEncoder[UpdateSet]())
  262. invalidBytes := []byte("invalid")
  263. jsonBytes1, _ := jsonEncoder.Encode(inputUpdateSet1)
  264. gZipJsonBytes1, _ := gZipJsonEncoder.Encode(inputUpdateSet1)
  265. tests := map[string]struct {
  266. ext string
  267. b []byte
  268. want *UpdateSet
  269. wantErr bool
  270. }{
  271. "json with invalid": {
  272. ext: "json",
  273. b: invalidBytes,
  274. want: nil,
  275. wantErr: true,
  276. },
  277. "json with json": {
  278. ext: "json",
  279. b: jsonBytes1,
  280. want: inputUpdateSet1,
  281. wantErr: false,
  282. },
  283. "json with gzipjson": {
  284. ext: "json",
  285. b: gZipJsonBytes1,
  286. want: nil,
  287. wantErr: true,
  288. },
  289. "json.gz with invalid": {
  290. ext: "json.gz",
  291. b: invalidBytes,
  292. want: nil,
  293. wantErr: true,
  294. },
  295. "json.gz with json": {
  296. ext: "json.gz",
  297. b: jsonBytes1,
  298. want: nil,
  299. wantErr: true,
  300. },
  301. "json.gz with gzipjson": {
  302. ext: "json.gz",
  303. b: gZipJsonBytes1,
  304. want: inputUpdateSet1,
  305. wantErr: false,
  306. },
  307. }
  308. for name, tt := range tests {
  309. t.Run(name, func(t *testing.T) {
  310. got, err := deserializeUpdateSet(tt.ext, tt.b)
  311. if (err != nil) != tt.wantErr {
  312. t.Errorf("deserializeUpdateSet() error = %v, wantErr %v", err, tt.wantErr)
  313. return
  314. }
  315. if !reflect.DeepEqual(got, tt.want) {
  316. t.Errorf("deserializeUpdateSet() got = %v, want %v", got, tt.want)
  317. }
  318. })
  319. }
  320. }