test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575
  1. package storage
  2. import (
  3. "fmt"
  4. "io"
  5. "os"
  6. "path"
  7. "path/filepath"
  8. "strings"
  9. "testing"
  10. "github.com/opencost/opencost/core/pkg/util/json"
  11. )
  12. type testFileContent struct {
  13. Field1 int `json:"field_1"`
  14. Field2 string `json:"field_2"`
  15. }
  16. var tfc = testFileContent{
  17. Field1: 101,
  18. Field2: "TEST_FILE_CONTENT",
  19. }
  20. const testpath = "opencost/storage/"
  21. func createFiles(files []string, testName string, store Storage) error {
  22. b, err := json.Marshal(tfc)
  23. if err != nil {
  24. return fmt.Errorf("failed to marshal file content: %w", err)
  25. }
  26. for _, fileName := range files {
  27. filePath := path.Join(testpath, testName, fileName)
  28. err = store.Write(filePath, b)
  29. if err != nil {
  30. return fmt.Errorf("failed to write file '%s': %w ", filePath, err)
  31. }
  32. }
  33. return nil
  34. }
  35. func cleanupFiles(files []string, testName string, store Storage) error {
  36. for _, fileName := range files {
  37. filePath := path.Join(testpath, testName, fileName)
  38. err := store.Remove(filePath)
  39. if err != nil {
  40. return fmt.Errorf("failed to remove file '%s': %w ", filePath, err)
  41. }
  42. }
  43. return nil
  44. }
  45. func TestStorageList(t *testing.T, store Storage) {
  46. testName := "list"
  47. fileNames := []string{
  48. "/file0.json",
  49. "/file1.json",
  50. "/dir0/file2.json",
  51. "/dir0/file3.json",
  52. }
  53. err := createFiles(fileNames, testName, store)
  54. if err != nil {
  55. t.Errorf("failed to create files: %s", err)
  56. }
  57. defer func() {
  58. err = cleanupFiles(fileNames, testName, store)
  59. if err != nil {
  60. t.Errorf("failed to clean up files: %s", err)
  61. }
  62. }()
  63. testCases := map[string]struct {
  64. path string
  65. expected []string
  66. expectErr bool
  67. }{
  68. "base dir files": {
  69. path: path.Join(testpath, testName),
  70. expected: []string{
  71. "file0.json",
  72. "file1.json",
  73. },
  74. expectErr: false,
  75. },
  76. "single nested dir files": {
  77. path: path.Join(testpath, testName, "dir0"),
  78. expected: []string{
  79. "file2.json",
  80. "file3.json",
  81. },
  82. expectErr: false,
  83. },
  84. "nonexistent dir files": {
  85. path: path.Join(testpath, testName, "dir1"),
  86. expected: []string{},
  87. expectErr: false,
  88. },
  89. }
  90. for name, tc := range testCases {
  91. t.Run(name, func(t *testing.T) {
  92. fileList, err := store.List(tc.path)
  93. if tc.expectErr == (err == nil) {
  94. if tc.expectErr {
  95. t.Errorf("expected error was not thrown")
  96. return
  97. }
  98. t.Errorf("unexpected error: %s", err.Error())
  99. return
  100. }
  101. if len(fileList) != len(tc.expected) {
  102. t.Errorf("file list length does not match expected length, actual: %d, expected: %d", len(fileList), len(tc.expected))
  103. }
  104. expectedSet := map[string]struct{}{}
  105. for _, expName := range tc.expected {
  106. expectedSet[expName] = struct{}{}
  107. }
  108. for _, file := range fileList {
  109. _, ok := expectedSet[file.Name]
  110. if !ok {
  111. t.Errorf("unexpect file in list %s", file.Name)
  112. }
  113. if file.Size == 0 {
  114. t.Errorf("file size is not set")
  115. }
  116. if file.ModTime.IsZero() {
  117. t.Errorf("file mod time is not set")
  118. }
  119. }
  120. })
  121. }
  122. }
  123. func TestStorageListDirectories(t *testing.T, store Storage) {
  124. testName := "list_directories"
  125. fileNames := []string{
  126. "/file0.json",
  127. "/dir0/file2.json",
  128. "/dir0/file3.json",
  129. "/dir0/dir1/file4.json",
  130. "/dir0/dir2/file5.json",
  131. }
  132. err := createFiles(fileNames, testName, store)
  133. if err != nil {
  134. t.Errorf("failed to create files: %s", err)
  135. }
  136. defer func() {
  137. err = cleanupFiles(fileNames, testName, store)
  138. if err != nil {
  139. t.Errorf("failed to clean up files: %s", err)
  140. }
  141. }()
  142. testCases := map[string]struct {
  143. path string
  144. expected []string
  145. expectErr bool
  146. }{
  147. "root dir dir": {
  148. path: "",
  149. expected: []string{
  150. strings.Split(testpath, "/")[0] + "/",
  151. },
  152. expectErr: false,
  153. },
  154. "base dir dir": {
  155. path: path.Join(testpath, testName),
  156. expected: []string{
  157. path.Join(testpath, testName, "dir0") + "/",
  158. },
  159. expectErr: false,
  160. },
  161. "single nested dir files": {
  162. path: path.Join(testpath, testName, "dir0"),
  163. expected: []string{
  164. path.Join(testpath, testName, "dir0", "dir1") + "/",
  165. path.Join(testpath, testName, "dir0", "dir2") + "/",
  166. },
  167. expectErr: false,
  168. },
  169. "dir with no sub dirs": {
  170. path: path.Join(testpath, testName, "dir0/dir1"),
  171. expected: []string{},
  172. expectErr: false,
  173. },
  174. "non-existent dir": {
  175. path: path.Join(testpath, testName, "dir1"),
  176. expected: []string{},
  177. expectErr: false,
  178. },
  179. }
  180. for name, tc := range testCases {
  181. t.Run(name, func(t *testing.T) {
  182. dirList, err := store.ListDirectories(tc.path)
  183. if tc.expectErr == (err == nil) {
  184. if tc.expectErr {
  185. t.Errorf("expected error was not thrown")
  186. return
  187. }
  188. t.Errorf("unexpected error: %s", err.Error())
  189. return
  190. }
  191. if len(dirList) != len(tc.expected) {
  192. t.Errorf("dir list length does not match expected length, actual: %d, expected: %d", len(dirList), len(tc.expected))
  193. }
  194. expectedSet := map[string]struct{}{}
  195. for _, expName := range tc.expected {
  196. expectedSet[expName] = struct{}{}
  197. }
  198. for _, dir := range dirList {
  199. _, ok := expectedSet[dir.Name]
  200. if !ok {
  201. t.Errorf("unexpect dir in list %s", dir.Name)
  202. }
  203. }
  204. })
  205. }
  206. }
  207. func TestStorageExists(t *testing.T, store Storage) {
  208. testName := "exists"
  209. fileNames := []string{
  210. "/file0.json",
  211. }
  212. err := createFiles(fileNames, testName, store)
  213. if err != nil {
  214. t.Errorf("failed to create files: %s", err)
  215. }
  216. defer func() {
  217. err = cleanupFiles(fileNames, testName, store)
  218. if err != nil {
  219. t.Errorf("failed to clean up files: %s", err)
  220. }
  221. }()
  222. testCases := map[string]struct {
  223. path string
  224. expected bool
  225. expectErr bool
  226. }{
  227. "file exists": {
  228. path: path.Join(testpath, testName, "file0.json"),
  229. expected: true,
  230. expectErr: false,
  231. },
  232. "file does not exist": {
  233. path: path.Join(testpath, testName, "file1.json"),
  234. expected: false,
  235. expectErr: false,
  236. },
  237. "dir does not exist": {
  238. path: path.Join(testpath, testName, "dir0/file.json"),
  239. expected: false,
  240. expectErr: false,
  241. },
  242. }
  243. for name, tc := range testCases {
  244. t.Run(name, func(t *testing.T) {
  245. exists, err := store.Exists(tc.path)
  246. if tc.expectErr == (err == nil) {
  247. if tc.expectErr {
  248. t.Errorf("expected error was not thrown")
  249. return
  250. }
  251. t.Errorf("unexpected error: %s", err.Error())
  252. return
  253. }
  254. if exists != tc.expected {
  255. t.Errorf("file exists output did not match expected")
  256. }
  257. })
  258. }
  259. }
  260. func TestStorageRead(t *testing.T, store Storage) {
  261. testName := "read"
  262. fileNames := []string{
  263. "/file0.json",
  264. }
  265. err := createFiles(fileNames, testName, store)
  266. if err != nil {
  267. t.Errorf("failed to create files: %s", err)
  268. }
  269. defer func() {
  270. err = cleanupFiles(fileNames, testName, store)
  271. if err != nil {
  272. t.Errorf("failed to clean up files: %s", err)
  273. }
  274. }()
  275. testCases := map[string]struct {
  276. path string
  277. expectErr bool
  278. }{
  279. "file exists": {
  280. path: path.Join(testpath, testName, "file0.json"),
  281. expectErr: false,
  282. },
  283. "file does not exist": {
  284. path: path.Join(testpath, testName, "file1.json"),
  285. expectErr: true,
  286. },
  287. "dir does not exist": {
  288. path: path.Join(testpath, testName, "dir0/file.json"),
  289. expectErr: true,
  290. },
  291. }
  292. for name, tc := range testCases {
  293. t.Run(name, func(t *testing.T) {
  294. b, err := store.Read(tc.path)
  295. if tc.expectErr && err != nil {
  296. return
  297. }
  298. if tc.expectErr == (err == nil) {
  299. if tc.expectErr {
  300. t.Errorf("expected error was not thrown")
  301. return
  302. }
  303. t.Errorf("unexpected error: %s", err.Error())
  304. return
  305. }
  306. var content testFileContent
  307. err = json.Unmarshal(b, &content)
  308. if err != nil {
  309. t.Errorf("could not unmarshal file content")
  310. return
  311. }
  312. if content != tfc {
  313. t.Errorf("file content did not match writen value")
  314. }
  315. })
  316. }
  317. }
  318. func TestStorageStat(t *testing.T, store Storage) {
  319. testName := "stat"
  320. fileNames := []string{
  321. "/file0.json",
  322. }
  323. err := createFiles(fileNames, testName, store)
  324. if err != nil {
  325. t.Errorf("failed to create files: %s", err)
  326. }
  327. defer func() {
  328. err = cleanupFiles(fileNames, testName, store)
  329. if err != nil {
  330. t.Errorf("failed to clean up files: %s", err)
  331. }
  332. }()
  333. testCases := map[string]struct {
  334. path string
  335. expected *StorageInfo
  336. expectErr bool
  337. }{
  338. "base dir": {
  339. path: path.Join(testpath, testName, "file0.json"),
  340. expected: &StorageInfo{
  341. Name: "file0.json",
  342. Size: 45,
  343. },
  344. expectErr: false,
  345. },
  346. "file does not exist": {
  347. path: path.Join(testpath, testName, "file1.json"),
  348. expected: nil,
  349. expectErr: true,
  350. },
  351. }
  352. for name, tc := range testCases {
  353. t.Run(name, func(t *testing.T) {
  354. status, err := store.Stat(tc.path)
  355. if tc.expectErr && err != nil {
  356. return
  357. }
  358. if tc.expectErr == (err == nil) {
  359. if tc.expectErr {
  360. t.Errorf("expected error was not thrown")
  361. return
  362. }
  363. t.Errorf("unexpected error: %s", err.Error())
  364. return
  365. }
  366. if status.Name != tc.expected.Name {
  367. t.Errorf("status name did name match expected, actual: %s, expected: %s", status.Name, tc.expected.Name)
  368. }
  369. if status.Size != tc.expected.Size {
  370. t.Errorf("status name did size match expected, actual: %d, expected: %d", status.Size, tc.expected.Size)
  371. }
  372. if status.ModTime.IsZero() {
  373. t.Errorf("status mod time is not set")
  374. }
  375. })
  376. }
  377. }
  378. func TestStorageReadToLocalFile(t *testing.T, store Storage) {
  379. testName := "read_to_local_file"
  380. fileNames := []string{
  381. "/file0.json",
  382. }
  383. err := createFiles(fileNames, testName, store)
  384. if err != nil {
  385. t.Fatalf("failed to create files: %s", err)
  386. }
  387. defer func() {
  388. err = cleanupFiles(fileNames, testName, store)
  389. if err != nil {
  390. t.Fatalf("failed to clean up files: %s", err)
  391. }
  392. }()
  393. testCases := map[string]struct {
  394. path string
  395. expectErr bool
  396. }{
  397. "file exists": {
  398. path: path.Join(testpath, testName, "file0.json"),
  399. expectErr: false,
  400. },
  401. "file does not exist": {
  402. path: path.Join(testpath, testName, "file1.json"),
  403. expectErr: true,
  404. },
  405. "dir does not exist": {
  406. path: path.Join(testpath, testName, "dir0/file.json"),
  407. expectErr: true,
  408. },
  409. }
  410. for name, tc := range testCases {
  411. t.Run(name, func(t *testing.T) {
  412. destPath := filepath.Join(t.TempDir(), "out.json")
  413. err := store.ReadToLocalFile(tc.path, destPath)
  414. if tc.expectErr {
  415. if err == nil {
  416. t.Fatalf("expected error was not thrown")
  417. }
  418. return
  419. }
  420. if err != nil {
  421. t.Fatalf("unexpected error: %s", err.Error())
  422. }
  423. b, err := os.ReadFile(destPath)
  424. if err != nil {
  425. t.Fatalf("reading destination file: %s", err)
  426. }
  427. var content testFileContent
  428. err = json.Unmarshal(b, &content)
  429. if err != nil {
  430. t.Fatalf("could not unmarshal file content: %s", err)
  431. }
  432. if content != tfc {
  433. t.Fatalf("file content did not match written value")
  434. }
  435. })
  436. }
  437. }
  438. func TestStorageReadStream(t *testing.T, store Storage) {
  439. testName := "read_stream"
  440. fileNames := []string{
  441. "/file0.json",
  442. }
  443. err := createFiles(fileNames, testName, store)
  444. if err != nil {
  445. t.Fatalf("failed to create files: %s", err)
  446. }
  447. defer func() {
  448. err = cleanupFiles(fileNames, testName, store)
  449. if err != nil {
  450. t.Fatalf("failed to clean up files: %s", err)
  451. }
  452. }()
  453. testCases := map[string]struct {
  454. path string
  455. expectErr bool
  456. }{
  457. "file exists": {
  458. path: path.Join(testpath, testName, "file0.json"),
  459. expectErr: false,
  460. },
  461. "file does not exist": {
  462. path: path.Join(testpath, testName, "file1.json"),
  463. expectErr: true,
  464. },
  465. "dir does not exist": {
  466. path: path.Join(testpath, testName, "dir0/file.json"),
  467. expectErr: true,
  468. },
  469. }
  470. for name, tc := range testCases {
  471. t.Run(name, func(t *testing.T) {
  472. r, err := store.ReadStream(tc.path)
  473. if tc.expectErr {
  474. if err == nil {
  475. if r != nil {
  476. _ = r.Close()
  477. }
  478. t.Fatalf("expected error was not thrown")
  479. }
  480. return
  481. }
  482. if err != nil {
  483. t.Fatalf("unexpected error: %s", err.Error())
  484. }
  485. defer r.Close()
  486. b, err := io.ReadAll(r)
  487. if err != nil {
  488. t.Fatalf("reading stream: %s", err)
  489. }
  490. var content testFileContent
  491. err = json.Unmarshal(b, &content)
  492. if err != nil {
  493. t.Fatalf("could not unmarshal file content: %s", err)
  494. }
  495. if content != tfc {
  496. t.Fatalf("file content did not match written value")
  497. }
  498. })
  499. }
  500. }