path_test.go 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. package pathing
  2. import (
  3. "fmt"
  4. "testing"
  5. "time"
  6. "github.com/opencost/opencost/core/pkg/opencost"
  7. "github.com/stretchr/testify/require"
  8. )
  9. func TestDefaultPathFormatter(t *testing.T) {
  10. type testCase struct {
  11. name string
  12. appName string
  13. clusterID string
  14. clusterName string
  15. pipeline string
  16. resolution *time.Duration
  17. prefix string
  18. expected string
  19. }
  20. testCases := []testCase{
  21. {
  22. name: "no resolution",
  23. appName: "test-app",
  24. clusterID: "clusterID-a",
  25. clusterName: "cluster-a",
  26. pipeline: "allocation",
  27. resolution: nil,
  28. prefix: "",
  29. expected: fmt.Sprintf("%s/cluster-a/%s/allocation/1704110400-1704114000", DefaultRootDir, BaseStorageDir),
  30. },
  31. {
  32. name: "with resolution",
  33. appName: "test-app",
  34. clusterID: "clusterID-a",
  35. clusterName: "cluster-a",
  36. pipeline: "allocation",
  37. resolution: &[]time.Duration{1 * time.Hour}[0],
  38. prefix: "",
  39. expected: fmt.Sprintf("%s/cluster-a/%s/allocation/1h/1704110400-1704114000", DefaultRootDir, BaseStorageDir),
  40. },
  41. {
  42. name: "no resolution with prefix",
  43. appName: "test-app",
  44. clusterID: "clusterID-a",
  45. clusterName: "cluster-a",
  46. pipeline: "allocation",
  47. resolution: nil,
  48. prefix: "test",
  49. expected: fmt.Sprintf("%s/cluster-a/%s/allocation/test.1704110400-1704114000", DefaultRootDir, BaseStorageDir),
  50. },
  51. {
  52. name: "with resolution with prefix",
  53. appName: "test-app",
  54. clusterID: "clusterID-a",
  55. clusterName: "cluster-a",
  56. pipeline: "allocation",
  57. resolution: &[]time.Duration{1 * time.Hour}[0],
  58. prefix: "test",
  59. expected: fmt.Sprintf("%s/cluster-a/%s/allocation/1h/test.1704110400-1704114000", DefaultRootDir, BaseStorageDir),
  60. },
  61. {
  62. name: "daily resolution",
  63. appName: "test-app",
  64. clusterID: "clusterID-a",
  65. clusterName: "cluster-a",
  66. pipeline: "allocation",
  67. resolution: &[]time.Duration{24 * time.Hour}[0],
  68. prefix: "",
  69. expected: fmt.Sprintf("%s/cluster-a/%s/allocation/1d/1704110400-1704196800", DefaultRootDir, BaseStorageDir),
  70. },
  71. {
  72. name: "weekly resolution",
  73. appName: "test-app",
  74. clusterID: "clusterID-a",
  75. clusterName: "cluster-a",
  76. pipeline: "allocation",
  77. resolution: &[]time.Duration{7 * 24 * time.Hour}[0],
  78. prefix: "",
  79. expected: fmt.Sprintf("%s/cluster-a/%s/allocation/1w/1704110400-1704715200", DefaultRootDir, BaseStorageDir),
  80. },
  81. }
  82. for _, tc := range testCases {
  83. t.Run(tc.name, func(t *testing.T) {
  84. pathing, err := NewDefaultStoragePathFormatter(tc.appName, tc.clusterID, tc.clusterName, tc.pipeline, tc.resolution)
  85. if err != nil {
  86. t.Fatalf("Unexpected error: %v", err)
  87. }
  88. start := time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC)
  89. end := time.Date(2024, 1, 1, 13, 0, 0, 0, time.UTC)
  90. if tc.resolution != nil {
  91. end = start.Add(*tc.resolution)
  92. }
  93. result := pathing.ToFullPath(tc.prefix, opencost.NewClosedWindow(start, end), "")
  94. if result != tc.expected {
  95. t.Errorf("Expected %s, got %s", tc.expected, result)
  96. }
  97. })
  98. }
  99. }
  100. func TestEventPathFormatter(t *testing.T) {
  101. type testCase struct {
  102. name string
  103. rootPath string
  104. clusterID string
  105. event string
  106. subPaths []string
  107. prefix string
  108. fileExt string
  109. expected string
  110. }
  111. testCases := []testCase{
  112. {
  113. name: "with root path with file extension",
  114. rootPath: "/tmp/root",
  115. clusterID: "cluster-a",
  116. event: "heartbeat",
  117. subPaths: []string{},
  118. prefix: "",
  119. fileExt: "json",
  120. expected: "/tmp/root/cluster-a/heartbeat/20240101124000.json",
  121. },
  122. {
  123. name: "with file extension",
  124. rootPath: "root",
  125. clusterID: "cluster-a",
  126. event: "heartbeat",
  127. subPaths: []string{},
  128. prefix: "",
  129. fileExt: "json",
  130. expected: "root/cluster-a/heartbeat/20240101124000.json",
  131. },
  132. {
  133. name: "with root path with file extension with sub-paths",
  134. rootPath: "/tmp/root",
  135. clusterID: "cluster-a",
  136. event: "heartbeat",
  137. subPaths: []string{"foo", "bar"},
  138. prefix: "",
  139. fileExt: "json",
  140. expected: "/tmp/root/cluster-a/heartbeat/foo/bar/20240101124000.json",
  141. },
  142. {
  143. name: "without file extension",
  144. rootPath: "root",
  145. clusterID: "cluster-a",
  146. event: "heartbeat",
  147. subPaths: []string{},
  148. prefix: "",
  149. fileExt: "",
  150. expected: "root/cluster-a/heartbeat/20240101124000",
  151. },
  152. {
  153. name: "with prefix with file extension",
  154. rootPath: "root",
  155. clusterID: "cluster-a",
  156. event: "heartbeat",
  157. subPaths: []string{},
  158. prefix: "test",
  159. fileExt: "json",
  160. expected: "root/cluster-a/heartbeat/test.20240101124000.json",
  161. },
  162. {
  163. name: "with prefix with file extension with sub-paths",
  164. rootPath: "root",
  165. clusterID: "cluster-a",
  166. event: "heartbeat",
  167. subPaths: []string{"foo", "bar", "baz"},
  168. prefix: "test",
  169. fileExt: "json",
  170. expected: "root/cluster-a/heartbeat/foo/bar/baz/test.20240101124000.json",
  171. },
  172. {
  173. name: "with prefix without file extension",
  174. rootPath: "root",
  175. clusterID: "cluster-a",
  176. event: "heartbeat",
  177. subPaths: []string{},
  178. prefix: "test",
  179. fileExt: "",
  180. expected: "root/cluster-a/heartbeat/test.20240101124000",
  181. },
  182. {
  183. name: "with prefix without file extension with sub-paths",
  184. rootPath: "root",
  185. clusterID: "cluster-a",
  186. event: "heartbeat",
  187. subPaths: []string{"foo"},
  188. prefix: "test",
  189. fileExt: "",
  190. expected: "root/cluster-a/heartbeat/foo/test.20240101124000",
  191. },
  192. }
  193. for _, tc := range testCases {
  194. t.Run(tc.name, func(t *testing.T) {
  195. pathing, err := NewEventStoragePathFormatter(tc.rootPath, tc.clusterID, tc.event, tc.subPaths...)
  196. if err != nil {
  197. t.Fatalf("Unexpected error: %v", err)
  198. }
  199. timestamp := time.Date(2024, 1, 1, 12, 40, 0, 0, time.UTC)
  200. result := pathing.ToFullPath(tc.prefix, timestamp, tc.fileExt)
  201. if result != tc.expected {
  202. t.Errorf("Expected %s, got %s", tc.expected, result)
  203. }
  204. })
  205. }
  206. }
  207. func TestKubeModelPathFormatter(t *testing.T) {
  208. type testCase struct {
  209. name string
  210. start time.Time
  211. rootDir string
  212. clusterID string
  213. resolution string
  214. prefix string
  215. exp string
  216. }
  217. rootDir := "/path/to/root"
  218. testCases := []testCase{
  219. {
  220. name: "10m no prefix",
  221. start: time.Date(2025, time.December, 15, 12, 0, 0, 0, time.UTC),
  222. rootDir: rootDir,
  223. clusterID: "96d1c1d0-2183-416c-b8f7-754f42fd461a",
  224. resolution: "10m",
  225. prefix: "",
  226. exp: fmt.Sprintf("%s/96d1c1d0-2183-416c-b8f7-754f42fd461a/kubemodel/%s/%s/%s", rootDir, "10m", "2025/12/15", "20251215120000"),
  227. },
  228. {
  229. name: "1h no prefix",
  230. start: time.Date(2025, time.December, 15, 12, 0, 0, 0, time.UTC),
  231. rootDir: rootDir,
  232. clusterID: "96d1c1d0-2183-416c-b8f7-754f42fd461a",
  233. resolution: "1h",
  234. prefix: "",
  235. exp: fmt.Sprintf("%s/96d1c1d0-2183-416c-b8f7-754f42fd461a/kubemodel/%s/%s/%s", rootDir, "1h", "2025/12/15", "20251215120000"),
  236. },
  237. {
  238. name: "1d no prefix",
  239. start: time.Date(2025, time.December, 15, 12, 0, 0, 0, time.UTC),
  240. rootDir: rootDir,
  241. clusterID: "96d1c1d0-2183-416c-b8f7-754f42fd461a",
  242. resolution: "1d",
  243. prefix: "",
  244. exp: fmt.Sprintf("%s/96d1c1d0-2183-416c-b8f7-754f42fd461a/kubemodel/%s/%s/%s", rootDir, "1d", "2025/12/15", "20251215120000"),
  245. },
  246. {
  247. name: "1d prefix",
  248. start: time.Date(2025, time.December, 15, 12, 0, 0, 0, time.UTC),
  249. rootDir: rootDir,
  250. clusterID: "96d1c1d0-2183-416c-b8f7-754f42fd461a",
  251. resolution: "1d",
  252. prefix: "pre",
  253. exp: fmt.Sprintf("%s/96d1c1d0-2183-416c-b8f7-754f42fd461a/kubemodel/%s/%s/%s", rootDir, "1d", "2025/12/15", "pre.20251215120000"),
  254. },
  255. }
  256. for _, tc := range testCases {
  257. t.Run(tc.name, func(t *testing.T) {
  258. pathing, err := NewKubeModelStoragePathFormatter(tc.rootDir, tc.clusterID, tc.resolution)
  259. if err != nil {
  260. t.Fatalf("Unexpected error: %v", err)
  261. }
  262. var dur time.Duration
  263. switch tc.resolution {
  264. case "10m":
  265. dur = 10 * time.Minute
  266. case "1h":
  267. dur = time.Hour
  268. case "1d":
  269. dur = 24 * time.Hour
  270. default:
  271. t.Errorf("unexpected resolution: %s", tc.resolution)
  272. }
  273. end := tc.start.Add(dur)
  274. // dir := pathing.Dir()
  275. act := pathing.ToFullPath(tc.prefix, opencost.NewClosedWindow(tc.start, end), "")
  276. require.Equal(t, tc.exp, act)
  277. })
  278. }
  279. }