Micro-service for file storage and processing written in Go
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

544 lines
14 KiB

  1. package pipelines
  2. import (
  3. "image"
  4. "os"
  5. "path/filepath"
  6. "testing"
  7. "github.com/geplauder/lithium/storage"
  8. "github.com/stretchr/testify/assert"
  9. )
  10. // pipeline deserialization
  11. func TestPipelineDeserialization(t *testing.T) {
  12. t.Run("Image pipeline deserialization is successful", func(t *testing.T) {
  13. const Payload string = `{
  14. "name": "example pipeline",
  15. "type": 0,
  16. "removeMetadata": false,
  17. "steps": [
  18. {
  19. "name": "resize image",
  20. "type": 0
  21. },
  22. {
  23. "name": "compress image",
  24. "type": 1
  25. }
  26. ]
  27. }`
  28. values := DeserializePipelines([][]byte{[]byte(Payload)})
  29. assert.Equal(t, 1, len(values), "Output should contain one element")
  30. assert.Equal(t, "example pipeline", values[0].GetName())
  31. assert.Equal(t, Image, values[0].GetType())
  32. })
  33. t.Run("Video pipelines deserialization is successful", func(t *testing.T) {
  34. const Payload string = `{
  35. "name": "example pipeline",
  36. "type": 1,
  37. "removeMetadata": false,
  38. "steps": [
  39. {
  40. "name": "resize image",
  41. "type": 0
  42. },
  43. {
  44. "name": "compress image",
  45. "type": 1
  46. }
  47. ]
  48. }`
  49. values := DeserializePipelines([][]byte{[]byte(Payload)})
  50. assert.Equal(t, 1, len(values), "Output should contain one element")
  51. assert.Equal(t, "example pipeline", values[0].GetName())
  52. assert.Equal(t, Video, values[0].GetType())
  53. })
  54. }
  55. // image pipeline steps
  56. func TestExecuteSteps(t *testing.T) {
  57. t.Run("Pipeline executes with no steps", func(t *testing.T) {
  58. const Bucket string = "pipeline_test_01"
  59. const Payload string = `{
  60. "name": "example pipeline",
  61. "type": 1,
  62. "removeMetadata": false,
  63. "steps": []
  64. }`
  65. wd, _ := os.Getwd()
  66. pipe := DeserializePipelines([][]byte{[]byte(Payload)})[0]
  67. storageProvider := storage.GetFileSystemStorageProvider("test", "..")
  68. // copy test file to storage bucket
  69. _, err := storageProvider.StoreExisting(Bucket, "source.jpg", filepath.Join(wd, "../tests/files/900x900.jpg"))
  70. assert.Nil(t, err, "Test file should be readable")
  71. assert.FileExists(t, storageProvider.GetPath(Bucket, "source.jpg"))
  72. // run pipeline steps
  73. dest, err := pipe.Run("source.jpg", Bucket, storageProvider)
  74. assert.Nil(t, err)
  75. assert.FileExists(t, storageProvider.GetPath(Bucket, dest))
  76. // clean up
  77. os.Remove(storageProvider.GetPath(Bucket, "source.jpg"))
  78. os.Remove(storageProvider.GetPath(Bucket, dest))
  79. })
  80. t.Run("Image resizing is successful", func(t *testing.T) {
  81. const Bucket string = "pipeline_test_02"
  82. const Payload string = `{
  83. "name": "example pipeline",
  84. "type": 1,
  85. "removeMetadata": false,
  86. "steps": [
  87. {
  88. "name": "resize image",
  89. "type": 0,
  90. "options": {
  91. "width": 1280,
  92. "height": 720,
  93. "upscale": false
  94. }
  95. }
  96. ]
  97. }`
  98. wd, _ := os.Getwd()
  99. pipe := DeserializePipelines([][]byte{[]byte(Payload)})[0]
  100. storageProvider := storage.GetFileSystemStorageProvider("test", "..")
  101. // copy test file to storage bucket
  102. _, err := storageProvider.StoreExisting(Bucket, "source.jpg", filepath.Join(wd, "../tests/files/900x900.jpg"))
  103. assert.Nil(t, err, "Test file should be readable")
  104. assert.FileExists(t, storageProvider.GetPath(Bucket, "source.jpg"))
  105. // run pipeline steps
  106. dest, err := pipe.Run("source.jpg", Bucket, storageProvider)
  107. assert.Nil(t, err)
  108. assert.FileExists(t, storageProvider.GetPath(Bucket, dest))
  109. // read image config
  110. file, err := storageProvider.OpenFile(Bucket, dest)
  111. assert.Nil(t, err)
  112. imgConf, _, err := image.DecodeConfig(file)
  113. assert.Nil(t, err)
  114. assert.Equal(t, 1280, imgConf.Width)
  115. assert.Equal(t, 720, imgConf.Height)
  116. // clean up
  117. os.Remove(storageProvider.GetPath(Bucket, "source.jpg"))
  118. os.Remove(storageProvider.GetPath(Bucket, dest))
  119. })
  120. t.Run("Image rotation step is successful", func(t *testing.T) {
  121. const Bucket string = "pipeline_test_03"
  122. const Payload string = `{
  123. "name": "example pipeline",
  124. "type": 1,
  125. "removeMetadata": false,
  126. "steps": [
  127. {
  128. "name": "rotate image",
  129. "type": 1,
  130. "options": {
  131. "angle": 90.0
  132. }
  133. }
  134. ]
  135. }`
  136. wd, _ := os.Getwd()
  137. pipe := DeserializePipelines([][]byte{[]byte(Payload)})[0]
  138. storageProvider := storage.GetFileSystemStorageProvider("test", "..")
  139. // copy test file to storage bucket
  140. _, err := storageProvider.StoreExisting(Bucket, "source.jpg", filepath.Join(wd, "../tests/files/800x500.jpg"))
  141. assert.Nil(t, err, "Test file should be readable")
  142. assert.FileExists(t, storageProvider.GetPath(Bucket, "source.jpg"))
  143. // run pipeline steps
  144. dest, err := pipe.Run("source.jpg", Bucket, storageProvider)
  145. assert.Nil(t, err)
  146. assert.FileExists(t, storageProvider.GetPath(Bucket, dest))
  147. // read image config
  148. file, err := storageProvider.OpenFile(Bucket, dest)
  149. assert.Nil(t, err)
  150. imgConf, _, err := image.DecodeConfig(file)
  151. assert.Nil(t, err)
  152. assert.Equal(t, 500, imgConf.Width)
  153. assert.Equal(t, 800, imgConf.Height)
  154. // clean up
  155. os.Remove(storageProvider.GetPath(Bucket, "source.jpg"))
  156. os.Remove(storageProvider.GetPath(Bucket, dest))
  157. })
  158. t.Run("Image flip step is successful", func(t *testing.T) {
  159. const Bucket string = "pipeline_test_06"
  160. const Payload string = `{
  161. "name": "example pipeline",
  162. "type": 1,
  163. "removeMetadata": false,
  164. "steps": [
  165. {
  166. "name": "flip image",
  167. "type": 2,
  168. "options": {
  169. "direction": "h"
  170. }
  171. }
  172. ]
  173. }`
  174. wd, _ := os.Getwd()
  175. pipe := DeserializePipelines([][]byte{[]byte(Payload)})[0]
  176. storageProvider := storage.GetFileSystemStorageProvider("test", "..")
  177. // copy test file to storage bucket
  178. _, err := storageProvider.StoreExisting(Bucket, "source.jpg", filepath.Join(wd, "../tests/files/800x500.jpg"))
  179. assert.Nil(t, err, "Test file should be readable")
  180. assert.FileExists(t, storageProvider.GetPath(Bucket, "source.jpg"))
  181. // run pipeline steps
  182. dest, err := pipe.Run("source.jpg", Bucket, storageProvider)
  183. assert.Nil(t, err)
  184. assert.FileExists(t, storageProvider.GetPath(Bucket, dest))
  185. // read image config
  186. file, err := storageProvider.OpenFile(Bucket, dest)
  187. assert.Nil(t, err)
  188. imgConf, _, err := image.DecodeConfig(file)
  189. assert.Nil(t, err)
  190. assert.Equal(t, 800, imgConf.Width)
  191. assert.Equal(t, 500, imgConf.Height)
  192. // clean up
  193. os.Remove(storageProvider.GetPath(Bucket, "source.jpg"))
  194. os.Remove(storageProvider.GetPath(Bucket, dest))
  195. })
  196. t.Run("Image flip step direction validation is successful", func(t *testing.T) {
  197. const Bucket string = "pipeline_test_07"
  198. const Payload string = `{
  199. "name": "example pipeline",
  200. "type": 1,
  201. "removeMetadata": false,
  202. "steps": [
  203. {
  204. "name": "flip image",
  205. "type": 2,
  206. "options": {
  207. "direction": "f"
  208. }
  209. }
  210. ]
  211. }`
  212. wd, _ := os.Getwd()
  213. pipe := DeserializePipelines([][]byte{[]byte(Payload)})[0]
  214. storageProvider := storage.GetFileSystemStorageProvider("test", "..")
  215. // copy test file to storage bucket
  216. _, err := storageProvider.StoreExisting(Bucket, "source.jpg", filepath.Join(wd, "../tests/files/800x500.jpg"))
  217. assert.Nil(t, err, "Test file should be readable")
  218. assert.FileExists(t, storageProvider.GetPath(Bucket, "source.jpg"))
  219. // run pipeline steps
  220. _, err = pipe.Run("source.jpg", Bucket, storageProvider)
  221. assert.EqualError(t, err, "invalid flip direction: f")
  222. // clean up
  223. os.Remove(storageProvider.GetPath(Bucket, "source.jpg"))
  224. })
  225. t.Run("Image grayscale step is successful", func(t *testing.T) {
  226. const Bucket string = "pipeline_test_05"
  227. const Payload string = `{
  228. "name": "example pipeline",
  229. "type": 1,
  230. "removeMetadata": false,
  231. "steps": [
  232. {
  233. "name": "grayscale",
  234. "type": 3
  235. }
  236. ]
  237. }`
  238. wd, _ := os.Getwd()
  239. pipe := DeserializePipelines([][]byte{[]byte(Payload)})[0]
  240. storageProvider := storage.GetFileSystemStorageProvider("test", "..")
  241. // copy test file to storage bucket
  242. _, err := storageProvider.StoreExisting(Bucket, "source.jpg", filepath.Join(wd, "../tests/files/900x900.jpg"))
  243. assert.Nil(t, err, "Test file should be readable")
  244. assert.FileExists(t, storageProvider.GetPath(Bucket, "source.jpg"))
  245. // run pipeline steps
  246. dest, err := pipe.Run("source.jpg", Bucket, storageProvider)
  247. assert.Nil(t, err)
  248. assert.FileExists(t, storageProvider.GetPath(Bucket, dest))
  249. // read image config
  250. file, err := storageProvider.OpenFile(Bucket, dest)
  251. assert.Nil(t, err)
  252. imgConf, _, err := image.DecodeConfig(file)
  253. assert.Nil(t, err)
  254. assert.Equal(t, 900, imgConf.Width)
  255. assert.Equal(t, 900, imgConf.Height)
  256. // clean up
  257. os.Remove(storageProvider.GetPath(Bucket, "source.jpg"))
  258. os.Remove(storageProvider.GetPath(Bucket, dest))
  259. })
  260. t.Run("Image fit step is successful", func(t *testing.T) {
  261. const Bucket string = "pipeline_test_08"
  262. const Payload string = `{
  263. "name": "example pipeline",
  264. "type": 1,
  265. "removeMetadata": false,
  266. "steps": [
  267. {
  268. "name": "fit",
  269. "type": 4,
  270. "options": {
  271. "width": 300,
  272. "height": 200
  273. }
  274. }
  275. ]
  276. }`
  277. wd, _ := os.Getwd()
  278. pipe := DeserializePipelines([][]byte{[]byte(Payload)})[0]
  279. storageProvider := storage.GetFileSystemStorageProvider("test", "..")
  280. // copy test file to storage bucket
  281. _, err := storageProvider.StoreExisting(Bucket, "source.jpg", filepath.Join(wd, "../tests/files/900x900.jpg"))
  282. assert.Nil(t, err, "Test file should be readable")
  283. assert.FileExists(t, storageProvider.GetPath(Bucket, "source.jpg"))
  284. // run pipeline steps
  285. dest, err := pipe.Run("source.jpg", Bucket, storageProvider)
  286. assert.Nil(t, err)
  287. assert.FileExists(t, storageProvider.GetPath(Bucket, dest))
  288. // read image config
  289. file, err := storageProvider.OpenFile(Bucket, dest)
  290. assert.Nil(t, err)
  291. imgConf, _, err := image.DecodeConfig(file)
  292. assert.Nil(t, err)
  293. assert.Equal(t, 200, imgConf.Width)
  294. assert.Equal(t, 200, imgConf.Height)
  295. // clean up
  296. os.Remove(storageProvider.GetPath(Bucket, "source.jpg"))
  297. os.Remove(storageProvider.GetPath(Bucket, dest))
  298. })
  299. t.Run("Image invert step is successful", func(t *testing.T) {
  300. const Bucket string = "pipeline_test_09"
  301. const Payload string = `{
  302. "name": "example pipeline",
  303. "type": 1,
  304. "removeMetadata": false,
  305. "steps": [
  306. {
  307. "name": "invert",
  308. "type": 5
  309. }
  310. ]
  311. }`
  312. wd, _ := os.Getwd()
  313. pipe := DeserializePipelines([][]byte{[]byte(Payload)})[0]
  314. storageProvider := storage.GetFileSystemStorageProvider("test", "..")
  315. // copy test file to storage bucket
  316. _, err := storageProvider.StoreExisting(Bucket, "source.jpg", filepath.Join(wd, "../tests/files/900x900.jpg"))
  317. assert.Nil(t, err, "Test file should be readable")
  318. assert.FileExists(t, storageProvider.GetPath(Bucket, "source.jpg"))
  319. // run pipeline steps
  320. dest, err := pipe.Run("source.jpg", Bucket, storageProvider)
  321. assert.Nil(t, err)
  322. assert.FileExists(t, storageProvider.GetPath(Bucket, dest))
  323. // read image config
  324. file, err := storageProvider.OpenFile(Bucket, dest)
  325. assert.Nil(t, err)
  326. imgConf, _, err := image.DecodeConfig(file)
  327. assert.Nil(t, err)
  328. assert.Equal(t, 900, imgConf.Width)
  329. assert.Equal(t, 900, imgConf.Height)
  330. // clean up
  331. os.Remove(storageProvider.GetPath(Bucket, "source.jpg"))
  332. os.Remove(storageProvider.GetPath(Bucket, dest))
  333. })
  334. t.Run("Image blur step is successful", func(t *testing.T) {
  335. const Bucket string = "pipeline_test_10"
  336. const Payload string = `{
  337. "name": "example pipeline",
  338. "type": 1,
  339. "removeMetadata": false,
  340. "steps": [
  341. {
  342. "name": "blur",
  343. "type": 6,
  344. "options": {
  345. "sigma": 50.0
  346. }
  347. }
  348. ]
  349. }`
  350. wd, _ := os.Getwd()
  351. pipe := DeserializePipelines([][]byte{[]byte(Payload)})[0]
  352. storageProvider := storage.GetFileSystemStorageProvider("test", "..")
  353. // copy test file to storage bucket
  354. _, err := storageProvider.StoreExisting(Bucket, "source.jpg", filepath.Join(wd, "../tests/files/900x900.jpg"))
  355. assert.Nil(t, err, "Test file should be readable")
  356. assert.FileExists(t, storageProvider.GetPath(Bucket, "source.jpg"))
  357. // run pipeline steps
  358. dest, err := pipe.Run("source.jpg", Bucket, storageProvider)
  359. assert.Nil(t, err)
  360. assert.FileExists(t, storageProvider.GetPath(Bucket, dest))
  361. // read image config
  362. file, err := storageProvider.OpenFile(Bucket, dest)
  363. assert.Nil(t, err)
  364. imgConf, _, err := image.DecodeConfig(file)
  365. assert.Nil(t, err)
  366. assert.Equal(t, 900, imgConf.Width)
  367. assert.Equal(t, 900, imgConf.Height)
  368. // clean up
  369. os.Remove(storageProvider.GetPath(Bucket, "source.jpg"))
  370. os.Remove(storageProvider.GetPath(Bucket, dest))
  371. })
  372. }
  373. // output options
  374. func TestEncoding(t *testing.T) {
  375. const Bucket string = "pipeline_test_04"
  376. const Payload string = `{
  377. "name": "example pipeline",
  378. "type": 1,
  379. "removeMetadata": false,
  380. "steps": [
  381. {
  382. "name": "resize image",
  383. "type": 0,
  384. "options": {
  385. "width": 1280,
  386. "height": 720,
  387. "upscale": false
  388. }
  389. }
  390. ],
  391. "output": {
  392. "quality": 50
  393. }
  394. }`
  395. t.Run("Image encoding with jpeg quality is successful", func(t *testing.T) {
  396. wd, _ := os.Getwd()
  397. pipe := DeserializePipelines([][]byte{[]byte(Payload)})[0]
  398. storageProvider := storage.GetFileSystemStorageProvider("test", "..")
  399. // copy test file to storage bucket
  400. _, err := storageProvider.StoreExisting(Bucket, "source.jpg", filepath.Join(wd, "../tests/files/900x900.jpg"))
  401. assert.Nil(t, err, "Test file should be readable")
  402. assert.FileExists(t, storageProvider.GetPath(Bucket, "source.jpg"))
  403. // run pipeline steps
  404. dest, err := pipe.Run("source.jpg", Bucket, storageProvider)
  405. assert.Nil(t, err)
  406. assert.FileExists(t, storageProvider.GetPath(Bucket, dest))
  407. // clean up
  408. os.Remove(storageProvider.GetPath(Bucket, "source.jpg"))
  409. os.Remove(storageProvider.GetPath(Bucket, dest))
  410. })
  411. t.Run("Wrong output format results in error", func(t *testing.T) {
  412. const InvalidPayload string = `{
  413. "name": "example pipeline",
  414. "type": 1,
  415. "removeMetadata": false,
  416. "steps": [
  417. {
  418. "name": "resize image",
  419. "type": 0,
  420. "options": {
  421. "width": 1280,
  422. "height": 720,
  423. "upscale": false
  424. }
  425. }
  426. ],
  427. "output": {
  428. "format": "foo",
  429. "quality": 50
  430. }
  431. }`
  432. wd, _ := os.Getwd()
  433. pipe := DeserializePipelines([][]byte{[]byte(InvalidPayload)})[0]
  434. storageProvider := storage.GetFileSystemStorageProvider("test", "..")
  435. // copy test file to storage bucket
  436. _, err := storageProvider.StoreExisting(Bucket, "source.jpg", filepath.Join(wd, "../tests/files/900x900.jpg"))
  437. assert.Nil(t, err, "Test file should be readable")
  438. assert.FileExists(t, storageProvider.GetPath(Bucket, "source.jpg"))
  439. // run pipeline steps
  440. _, err = pipe.Run("source.jpg", Bucket, storageProvider)
  441. assert.EqualError(t, err, "output format 'foo' is not supported")
  442. // clean up
  443. os.Remove(storageProvider.GetPath(Bucket, "source.jpg"))
  444. })
  445. }