parse_test.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. package test
  2. import (
  3. "context"
  4. "fmt"
  5. "os"
  6. "testing"
  7. "google.golang.org/protobuf/encoding/protojson"
  8. "k8s.io/utils/pointer"
  9. porterv1 "github.com/porter-dev/api-contracts/generated/go/porter/v1"
  10. "github.com/porter-dev/porter/internal/porter_app"
  11. "github.com/sergi/go-diff/diffmatchpatch"
  12. "github.com/matryer/is"
  13. )
  14. func TestParseYAML(t *testing.T) {
  15. tests := []struct {
  16. porterYamlFileName string
  17. want []*porterv1.PorterApp
  18. }{
  19. {"v2_input_nobuild", result_nobuild},
  20. {"v2_input_multi_app", result_multi_app},
  21. {"v1_input_no_build_no_image", v1_result_nobuild_no_image},
  22. }
  23. for _, tt := range tests {
  24. t.Run(tt.porterYamlFileName, func(t *testing.T) {
  25. is := is.New(t)
  26. want, err := os.ReadFile(fmt.Sprintf("../testdata/%s.yaml", tt.porterYamlFileName))
  27. is.NoErr(err) // no error expected reading test file
  28. got, err := porter_app.ParseYAML(context.Background(), want, "test-app")
  29. is.NoErr(err) // umbrella chart values should convert to map[string]any without issues
  30. for i, app := range got {
  31. diffProtoWithFailTest(t, is, tt.want[i], app.AppProto)
  32. is.Equal(app.EnvVariables, map[string]string{
  33. "PORT": "8080",
  34. "NODE_ENV": "production",
  35. })
  36. }
  37. })
  38. }
  39. }
  40. var result_nobuild = []*porterv1.PorterApp{{
  41. Name: "test-app",
  42. Services: map[string]*porterv1.Service{
  43. "example-web": {
  44. Name: "example-web",
  45. RunOptional: pointer.String("node index.js"),
  46. Instances: 0,
  47. Port: 8080,
  48. CpuCores: 0.1,
  49. RamMegabytes: 256,
  50. Config: &porterv1.Service_WebConfig{
  51. WebConfig: &porterv1.WebServiceConfig{
  52. Autoscaling: &porterv1.Autoscaling{
  53. Enabled: true,
  54. MinInstances: 1,
  55. MaxInstances: 3,
  56. CpuThresholdPercent: 60,
  57. MemoryThresholdPercent: 60,
  58. },
  59. Domains: []*porterv1.Domain{
  60. {
  61. Name: "test1.example.com",
  62. },
  63. {
  64. Name: "test2.example.com",
  65. },
  66. },
  67. HealthCheck: &porterv1.HealthCheck{
  68. Enabled: true,
  69. HttpPath: "/healthz",
  70. },
  71. },
  72. },
  73. Type: 1,
  74. },
  75. "example-wkr": {
  76. Name: "example-wkr",
  77. RunOptional: pointer.String("echo 'work'"),
  78. Instances: 1,
  79. Port: 80,
  80. CpuCores: 0.1,
  81. RamMegabytes: 256,
  82. Config: &porterv1.Service_WorkerConfig{
  83. WorkerConfig: &porterv1.WorkerServiceConfig{
  84. Autoscaling: nil,
  85. },
  86. },
  87. Type: 2,
  88. },
  89. "example-job": {
  90. Name: "example-job",
  91. RunOptional: pointer.String("echo 'hello world'"),
  92. CpuCores: 0.1,
  93. RamMegabytes: 256,
  94. Config: &porterv1.Service_JobConfig{
  95. JobConfig: &porterv1.JobServiceConfig{
  96. AllowConcurrentOptional: pointer.Bool(true),
  97. Cron: "*/10 * * * *",
  98. SuspendCron: pointer.Bool(false),
  99. TimeoutSeconds: 60,
  100. },
  101. },
  102. Type: 3,
  103. },
  104. },
  105. ServiceList: []*porterv1.Service{
  106. {
  107. Name: "example-web",
  108. RunOptional: pointer.String("node index.js"),
  109. Instances: 0,
  110. Port: 8080,
  111. CpuCores: 0.1,
  112. RamMegabytes: 256,
  113. Config: &porterv1.Service_WebConfig{
  114. WebConfig: &porterv1.WebServiceConfig{
  115. Autoscaling: &porterv1.Autoscaling{
  116. Enabled: true,
  117. MinInstances: 1,
  118. MaxInstances: 3,
  119. CpuThresholdPercent: 60,
  120. MemoryThresholdPercent: 60,
  121. },
  122. Domains: []*porterv1.Domain{
  123. {
  124. Name: "test1.example.com",
  125. },
  126. {
  127. Name: "test2.example.com",
  128. },
  129. },
  130. HealthCheck: &porterv1.HealthCheck{
  131. Enabled: true,
  132. HttpPath: "/healthz",
  133. },
  134. },
  135. },
  136. Type: 1,
  137. },
  138. {
  139. Name: "example-wkr",
  140. RunOptional: pointer.String("echo 'work'"),
  141. Instances: 1,
  142. Port: 80,
  143. CpuCores: 0.1,
  144. RamMegabytes: 256,
  145. Config: &porterv1.Service_WorkerConfig{
  146. WorkerConfig: &porterv1.WorkerServiceConfig{
  147. Autoscaling: nil,
  148. },
  149. },
  150. Type: 2,
  151. },
  152. {
  153. Name: "example-job",
  154. RunOptional: pointer.String("echo 'hello world'"),
  155. CpuCores: 0.1,
  156. RamMegabytes: 256,
  157. Config: &porterv1.Service_JobConfig{
  158. JobConfig: &porterv1.JobServiceConfig{
  159. AllowConcurrentOptional: pointer.Bool(true),
  160. Cron: "*/10 * * * *",
  161. SuspendCron: pointer.Bool(false),
  162. TimeoutSeconds: 60,
  163. },
  164. },
  165. Type: 3,
  166. },
  167. },
  168. Predeploy: &porterv1.Service{
  169. RunOptional: pointer.String("ls"),
  170. Instances: 0,
  171. Port: 0,
  172. CpuCores: 0,
  173. RamMegabytes: 0,
  174. Config: &porterv1.Service_JobConfig{},
  175. Type: 3,
  176. },
  177. Image: &porterv1.AppImage{
  178. Repository: "nginx",
  179. Tag: "latest",
  180. },
  181. }}
  182. var result_multi_app = []*porterv1.PorterApp{
  183. result_nobuild[0],
  184. {
  185. Name: "next-test",
  186. Services: map[string]*porterv1.Service{
  187. "example-web": {
  188. Name: "example-web",
  189. RunOptional: pointer.String("node index.js"),
  190. Instances: 0,
  191. Port: 8080,
  192. CpuCores: 0.1,
  193. RamMegabytes: 256,
  194. Config: &porterv1.Service_WebConfig{
  195. WebConfig: &porterv1.WebServiceConfig{
  196. Autoscaling: &porterv1.Autoscaling{
  197. Enabled: true,
  198. MinInstances: 1,
  199. MaxInstances: 3,
  200. CpuThresholdPercent: 60,
  201. MemoryThresholdPercent: 60,
  202. },
  203. Domains: []*porterv1.Domain{
  204. {
  205. Name: "test1.example.com",
  206. },
  207. {
  208. Name: "test2.example.com",
  209. },
  210. },
  211. HealthCheck: &porterv1.HealthCheck{
  212. Enabled: true,
  213. HttpPath: "/healthz",
  214. },
  215. },
  216. },
  217. Type: 1,
  218. },
  219. },
  220. ServiceList: []*porterv1.Service{
  221. {
  222. Name: "example-web",
  223. RunOptional: pointer.String("node index.js"),
  224. Instances: 0,
  225. Port: 8080,
  226. CpuCores: 0.1,
  227. RamMegabytes: 256,
  228. Config: &porterv1.Service_WebConfig{
  229. WebConfig: &porterv1.WebServiceConfig{
  230. Autoscaling: &porterv1.Autoscaling{
  231. Enabled: true,
  232. MinInstances: 1,
  233. MaxInstances: 3,
  234. CpuThresholdPercent: 60,
  235. MemoryThresholdPercent: 60,
  236. },
  237. Domains: []*porterv1.Domain{
  238. {
  239. Name: "test1.example.com",
  240. },
  241. {
  242. Name: "test2.example.com",
  243. },
  244. },
  245. HealthCheck: &porterv1.HealthCheck{
  246. Enabled: true,
  247. HttpPath: "/healthz",
  248. },
  249. },
  250. },
  251. Type: 1,
  252. },
  253. },
  254. Build: &porterv1.Build{
  255. Method: "docker",
  256. Context: "./",
  257. Dockerfile: "Dockerfile",
  258. },
  259. },
  260. }
  261. var v1_result_nobuild_no_image = []*porterv1.PorterApp{{
  262. Name: "test-app",
  263. Services: map[string]*porterv1.Service{
  264. "example-job": {
  265. Name: "example-job",
  266. RunOptional: pointer.String("echo 'hello world'"),
  267. CpuCores: 0.1,
  268. RamMegabytes: 256,
  269. Config: &porterv1.Service_JobConfig{
  270. JobConfig: &porterv1.JobServiceConfig{
  271. AllowConcurrent: true,
  272. Cron: "*/10 * * * *",
  273. },
  274. },
  275. Type: 3,
  276. },
  277. "example-wkr": {
  278. Name: "example-wkr",
  279. RunOptional: pointer.String("echo 'work'"),
  280. Instances: 1,
  281. Port: 80,
  282. CpuCores: 0.1,
  283. RamMegabytes: 256,
  284. Config: &porterv1.Service_WorkerConfig{
  285. WorkerConfig: &porterv1.WorkerServiceConfig{
  286. Autoscaling: nil,
  287. },
  288. },
  289. Type: 2,
  290. },
  291. "example-web": {
  292. Name: "example-web",
  293. RunOptional: pointer.String("node index.js"),
  294. Instances: 0,
  295. Port: 8080,
  296. CpuCores: 0.1,
  297. RamMegabytes: 256,
  298. Config: &porterv1.Service_WebConfig{
  299. WebConfig: &porterv1.WebServiceConfig{
  300. Autoscaling: &porterv1.Autoscaling{
  301. Enabled: true,
  302. MinInstances: 1,
  303. MaxInstances: 3,
  304. CpuThresholdPercent: 60,
  305. MemoryThresholdPercent: 60,
  306. },
  307. Domains: []*porterv1.Domain{
  308. {
  309. Name: "test1.example.com",
  310. },
  311. {
  312. Name: "test2.example.com",
  313. },
  314. },
  315. HealthCheck: &porterv1.HealthCheck{
  316. Enabled: true,
  317. HttpPath: "/healthz",
  318. },
  319. Private: pointer.Bool(false),
  320. },
  321. },
  322. Type: 1,
  323. },
  324. },
  325. ServiceList: []*porterv1.Service{
  326. {
  327. Name: "example-job",
  328. RunOptional: pointer.String("echo 'hello world'"),
  329. CpuCores: 0.1,
  330. RamMegabytes: 256,
  331. Config: &porterv1.Service_JobConfig{
  332. JobConfig: &porterv1.JobServiceConfig{
  333. AllowConcurrent: true,
  334. Cron: "*/10 * * * *",
  335. },
  336. },
  337. Type: 3,
  338. },
  339. {
  340. Name: "example-wkr",
  341. RunOptional: pointer.String("echo 'work'"),
  342. Instances: 1,
  343. Port: 80,
  344. CpuCores: 0.1,
  345. RamMegabytes: 256,
  346. Config: &porterv1.Service_WorkerConfig{
  347. WorkerConfig: &porterv1.WorkerServiceConfig{
  348. Autoscaling: nil,
  349. },
  350. },
  351. Type: 2,
  352. },
  353. {
  354. Name: "example-web",
  355. RunOptional: pointer.String("node index.js"),
  356. Instances: 0,
  357. Port: 8080,
  358. CpuCores: 0.1,
  359. RamMegabytes: 256,
  360. Config: &porterv1.Service_WebConfig{
  361. WebConfig: &porterv1.WebServiceConfig{
  362. Autoscaling: &porterv1.Autoscaling{
  363. Enabled: true,
  364. MinInstances: 1,
  365. MaxInstances: 3,
  366. CpuThresholdPercent: 60,
  367. MemoryThresholdPercent: 60,
  368. },
  369. Domains: []*porterv1.Domain{
  370. {
  371. Name: "test1.example.com",
  372. },
  373. {
  374. Name: "test2.example.com",
  375. },
  376. },
  377. HealthCheck: &porterv1.HealthCheck{
  378. Enabled: true,
  379. HttpPath: "/healthz",
  380. },
  381. Private: pointer.Bool(false),
  382. },
  383. },
  384. Type: 1,
  385. },
  386. },
  387. Predeploy: &porterv1.Service{
  388. RunOptional: pointer.String("ls"),
  389. Instances: 0,
  390. Port: 0,
  391. CpuCores: 0,
  392. RamMegabytes: 0,
  393. Config: &porterv1.Service_JobConfig{},
  394. Type: 3,
  395. },
  396. }}
  397. func diffProtoWithFailTest(t *testing.T, is *is.I, want, got *porterv1.PorterApp) {
  398. t.Helper()
  399. opts := protojson.MarshalOptions{Multiline: true}
  400. wantJson, err := opts.Marshal(want)
  401. is.NoErr(err) // no error expected marshalling want
  402. gotJson, err := opts.Marshal(got)
  403. is.NoErr(err) // no error expected marshalling got
  404. if string(wantJson) != string(gotJson) {
  405. dmp := diffmatchpatch.New()
  406. diffs := dmp.DiffMain(string(wantJson), string(gotJson), false)
  407. t.Errorf("diff between want and got: %s", dmp.DiffPrettyText(diffs))
  408. }
  409. }