@frictionless-ts/dataset 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) hide show
  1. package/README.md +3 -0
  2. package/build/file/copy.d.ts +4 -0
  3. package/build/file/copy.js +7 -0
  4. package/build/file/copy.spec.d.ts +1 -0
  5. package/build/file/copy.spec.js +104 -0
  6. package/build/file/describe.d.ts +7 -0
  7. package/build/file/describe.js +9 -0
  8. package/build/file/fetch.d.ts +2 -0
  9. package/build/file/fetch.js +21 -0
  10. package/build/file/index.d.ts +9 -0
  11. package/build/file/index.js +10 -0
  12. package/build/file/infer.d.ts +10 -0
  13. package/build/file/infer.js +48 -0
  14. package/build/file/infer.spec.d.ts +1 -0
  15. package/build/file/infer.spec.js +141 -0
  16. package/build/file/load.d.ts +3 -0
  17. package/build/file/load.js +7 -0
  18. package/build/file/path.d.ts +2 -0
  19. package/build/file/path.js +17 -0
  20. package/build/file/save.d.ts +3 -0
  21. package/build/file/save.js +7 -0
  22. package/build/file/temp.d.ts +11 -0
  23. package/build/file/temp.js +23 -0
  24. package/build/file/validate.d.ts +6 -0
  25. package/build/file/validate.js +47 -0
  26. package/build/file/validate.spec.d.ts +1 -0
  27. package/build/file/validate.spec.js +180 -0
  28. package/build/folder/create.d.ts +1 -0
  29. package/build/folder/create.js +5 -0
  30. package/build/folder/index.d.ts +2 -0
  31. package/build/folder/index.js +3 -0
  32. package/build/folder/temp.d.ts +3 -0
  33. package/build/folder/temp.js +16 -0
  34. package/build/index.d.ts +36 -0
  35. package/build/index.js +35 -0
  36. package/build/package/index.d.ts +2 -0
  37. package/build/package/index.js +3 -0
  38. package/build/package/merge.d.ts +23 -0
  39. package/build/package/merge.js +12 -0
  40. package/build/package/path.d.ts +3 -0
  41. package/build/package/path.js +43 -0
  42. package/build/package/path.spec.d.ts +1 -0
  43. package/build/package/path.spec.js +56 -0
  44. package/build/plugin.d.ts +11 -0
  45. package/build/plugin.js +2 -0
  46. package/build/plugins/ckan/ckan/index.d.ts +1 -0
  47. package/build/plugins/ckan/ckan/index.js +2 -0
  48. package/build/plugins/ckan/ckan/request.d.ts +11 -0
  49. package/build/plugins/ckan/ckan/request.js +35 -0
  50. package/build/plugins/ckan/index.d.ts +4 -0
  51. package/build/plugins/ckan/index.js +5 -0
  52. package/build/plugins/ckan/package/Organization.d.ts +21 -0
  53. package/build/plugins/ckan/package/Organization.js +2 -0
  54. package/build/plugins/ckan/package/Package.d.ts +76 -0
  55. package/build/plugins/ckan/package/Package.js +2 -0
  56. package/build/plugins/ckan/package/Tag.d.ts +17 -0
  57. package/build/plugins/ckan/package/Tag.js +2 -0
  58. package/build/plugins/ckan/package/convert/fromCkan.d.ts +3 -0
  59. package/build/plugins/ckan/package/convert/fromCkan.js +63 -0
  60. package/build/plugins/ckan/package/convert/fromCkan.spec.d.ts +1 -0
  61. package/build/plugins/ckan/package/convert/fromCkan.spec.js +80 -0
  62. package/build/plugins/ckan/package/convert/toCkan.d.ts +22 -0
  63. package/build/plugins/ckan/package/convert/toCkan.js +51 -0
  64. package/build/plugins/ckan/package/convert/toCkan.spec.d.ts +1 -0
  65. package/build/plugins/ckan/package/convert/toCkan.spec.js +153 -0
  66. package/build/plugins/ckan/package/fixtures/ckan-package.json +308 -0
  67. package/build/plugins/ckan/package/index.d.ts +7 -0
  68. package/build/plugins/ckan/package/index.js +5 -0
  69. package/build/plugins/ckan/package/load.d.ts +21 -0
  70. package/build/plugins/ckan/package/load.js +74 -0
  71. package/build/plugins/ckan/package/load.spec.d.ts +1 -0
  72. package/build/plugins/ckan/package/load.spec.js +11 -0
  73. package/build/plugins/ckan/package/save.d.ts +10 -0
  74. package/build/plugins/ckan/package/save.js +83 -0
  75. package/build/plugins/ckan/package/save.spec.d.ts +1 -0
  76. package/build/plugins/ckan/package/save.spec.js +319 -0
  77. package/build/plugins/ckan/plugin.d.ts +19 -0
  78. package/build/plugins/ckan/plugin.js +18 -0
  79. package/build/plugins/ckan/plugin.spec.d.ts +1 -0
  80. package/build/plugins/ckan/plugin.spec.js +83 -0
  81. package/build/plugins/ckan/resource/Resource.d.ts +56 -0
  82. package/build/plugins/ckan/resource/Resource.js +2 -0
  83. package/build/plugins/ckan/resource/convert/fromCkan.d.ts +3 -0
  84. package/build/plugins/ckan/resource/convert/fromCkan.js +39 -0
  85. package/build/plugins/ckan/resource/convert/toCkan.d.ts +3 -0
  86. package/build/plugins/ckan/resource/convert/toCkan.js +20 -0
  87. package/build/plugins/ckan/resource/index.d.ts +3 -0
  88. package/build/plugins/ckan/resource/index.js +3 -0
  89. package/build/plugins/ckan/schema/Field.d.ts +34 -0
  90. package/build/plugins/ckan/schema/Field.js +2 -0
  91. package/build/plugins/ckan/schema/Schema.d.ts +10 -0
  92. package/build/plugins/ckan/schema/Schema.js +2 -0
  93. package/build/plugins/ckan/schema/convert/fixtures/ckan-schema.json +115 -0
  94. package/build/plugins/ckan/schema/convert/fromCkan.d.ts +3 -0
  95. package/build/plugins/ckan/schema/convert/fromCkan.js +47 -0
  96. package/build/plugins/ckan/schema/convert/fromCkan.spec.d.ts +1 -0
  97. package/build/plugins/ckan/schema/convert/fromCkan.spec.js +157 -0
  98. package/build/plugins/ckan/schema/convert/toCkan.d.ts +3 -0
  99. package/build/plugins/ckan/schema/convert/toCkan.js +50 -0
  100. package/build/plugins/ckan/schema/convert/toCkan.spec.d.ts +1 -0
  101. package/build/plugins/ckan/schema/convert/toCkan.spec.js +278 -0
  102. package/build/plugins/ckan/schema/index.d.ts +4 -0
  103. package/build/plugins/ckan/schema/index.js +3 -0
  104. package/build/plugins/datahub/index.d.ts +2 -0
  105. package/build/plugins/datahub/index.js +3 -0
  106. package/build/plugins/datahub/package/index.d.ts +1 -0
  107. package/build/plugins/datahub/package/index.js +2 -0
  108. package/build/plugins/datahub/package/load.d.ts +1 -0
  109. package/build/plugins/datahub/package/load.js +9 -0
  110. package/build/plugins/datahub/package/load.spec.d.ts +1 -0
  111. package/build/plugins/datahub/package/load.spec.js +11 -0
  112. package/build/plugins/datahub/plugin.d.ts +4 -0
  113. package/build/plugins/datahub/plugin.js +18 -0
  114. package/build/plugins/datahub/plugin.spec.d.ts +1 -0
  115. package/build/plugins/datahub/plugin.spec.js +78 -0
  116. package/build/plugins/descriptor/index.d.ts +1 -0
  117. package/build/plugins/descriptor/index.js +2 -0
  118. package/build/plugins/descriptor/plugin.d.ts +11 -0
  119. package/build/plugins/descriptor/plugin.js +29 -0
  120. package/build/plugins/descriptor/plugin.spec.d.ts +1 -0
  121. package/build/plugins/descriptor/plugin.spec.js +169 -0
  122. package/build/plugins/folder/index.d.ts +2 -0
  123. package/build/plugins/folder/index.js +3 -0
  124. package/build/plugins/folder/package/index.d.ts +2 -0
  125. package/build/plugins/folder/package/index.js +3 -0
  126. package/build/plugins/folder/package/load.d.ts +1 -0
  127. package/build/plugins/folder/package/load.js +6 -0
  128. package/build/plugins/folder/package/load.spec.d.ts +1 -0
  129. package/build/plugins/folder/package/load.spec.js +175 -0
  130. package/build/plugins/folder/package/save.d.ts +7 -0
  131. package/build/plugins/folder/package/save.js +35 -0
  132. package/build/plugins/folder/package/save.spec.d.ts +1 -0
  133. package/build/plugins/folder/package/save.spec.js +328 -0
  134. package/build/plugins/folder/plugin.d.ts +4 -0
  135. package/build/plugins/folder/plugin.js +19 -0
  136. package/build/plugins/folder/plugin.spec.d.ts +1 -0
  137. package/build/plugins/folder/plugin.spec.js +53 -0
  138. package/build/plugins/github/github/index.d.ts +1 -0
  139. package/build/plugins/github/github/index.js +2 -0
  140. package/build/plugins/github/github/path.d.ts +1 -0
  141. package/build/plugins/github/github/path.js +4 -0
  142. package/build/plugins/github/github/request.d.ts +15 -0
  143. package/build/plugins/github/github/request.js +43 -0
  144. package/build/plugins/github/index.d.ts +3 -0
  145. package/build/plugins/github/index.js +4 -0
  146. package/build/plugins/github/package/License.d.ts +21 -0
  147. package/build/plugins/github/package/License.js +2 -0
  148. package/build/plugins/github/package/Owner.d.ts +25 -0
  149. package/build/plugins/github/package/Owner.js +2 -0
  150. package/build/plugins/github/package/Package.d.ts +87 -0
  151. package/build/plugins/github/package/Package.js +2 -0
  152. package/build/plugins/github/package/convert/fromGithub.d.ts +3 -0
  153. package/build/plugins/github/package/convert/fromGithub.js +50 -0
  154. package/build/plugins/github/package/index.d.ts +6 -0
  155. package/build/plugins/github/package/index.js +4 -0
  156. package/build/plugins/github/package/load.d.ts +23 -0
  157. package/build/plugins/github/package/load.js +48 -0
  158. package/build/plugins/github/package/load.spec.d.ts +1 -0
  159. package/build/plugins/github/package/load.spec.js +15 -0
  160. package/build/plugins/github/package/save.d.ts +14 -0
  161. package/build/plugins/github/package/save.js +69 -0
  162. package/build/plugins/github/package/save.spec.d.ts +1 -0
  163. package/build/plugins/github/package/save.spec.js +566 -0
  164. package/build/plugins/github/plugin.d.ts +19 -0
  165. package/build/plugins/github/plugin.js +18 -0
  166. package/build/plugins/github/plugin.spec.d.ts +1 -0
  167. package/build/plugins/github/plugin.spec.js +73 -0
  168. package/build/plugins/github/resource/Resource.d.ts +29 -0
  169. package/build/plugins/github/resource/Resource.js +2 -0
  170. package/build/plugins/github/resource/convert/fromGithub.d.ts +5 -0
  171. package/build/plugins/github/resource/convert/fromGithub.js +24 -0
  172. package/build/plugins/github/resource/convert/toGithub.d.ts +3 -0
  173. package/build/plugins/github/resource/convert/toGithub.js +10 -0
  174. package/build/plugins/github/resource/index.d.ts +3 -0
  175. package/build/plugins/github/resource/index.js +3 -0
  176. package/build/plugins/zenodo/index.d.ts +3 -0
  177. package/build/plugins/zenodo/index.js +4 -0
  178. package/build/plugins/zenodo/package/Creator.d.ts +20 -0
  179. package/build/plugins/zenodo/package/Creator.js +2 -0
  180. package/build/plugins/zenodo/package/Package.d.ts +94 -0
  181. package/build/plugins/zenodo/package/Package.js +2 -0
  182. package/build/plugins/zenodo/package/convert/fromZenodo.d.ts +3 -0
  183. package/build/plugins/zenodo/package/convert/fromZenodo.js +43 -0
  184. package/build/plugins/zenodo/package/convert/toZenodo.d.ts +3 -0
  185. package/build/plugins/zenodo/package/convert/toZenodo.js +79 -0
  186. package/build/plugins/zenodo/package/index.d.ts +6 -0
  187. package/build/plugins/zenodo/package/index.js +5 -0
  188. package/build/plugins/zenodo/package/load.d.ts +23 -0
  189. package/build/plugins/zenodo/package/load.js +45 -0
  190. package/build/plugins/zenodo/package/load.spec.d.ts +1 -0
  191. package/build/plugins/zenodo/package/load.spec.js +15 -0
  192. package/build/plugins/zenodo/package/save.d.ts +13 -0
  193. package/build/plugins/zenodo/package/save.js +74 -0
  194. package/build/plugins/zenodo/package/save.spec.d.ts +1 -0
  195. package/build/plugins/zenodo/package/save.spec.js +524 -0
  196. package/build/plugins/zenodo/plugin.d.ts +19 -0
  197. package/build/plugins/zenodo/plugin.js +18 -0
  198. package/build/plugins/zenodo/plugin.spec.d.ts +1 -0
  199. package/build/plugins/zenodo/plugin.spec.js +78 -0
  200. package/build/plugins/zenodo/resource/Resource.d.ts +27 -0
  201. package/build/plugins/zenodo/resource/Resource.js +2 -0
  202. package/build/plugins/zenodo/resource/convert/fromZenodo.d.ts +10 -0
  203. package/build/plugins/zenodo/resource/convert/fromZenodo.js +18 -0
  204. package/build/plugins/zenodo/resource/convert/toZenodo.d.ts +3 -0
  205. package/build/plugins/zenodo/resource/convert/toZenodo.js +13 -0
  206. package/build/plugins/zenodo/resource/index.d.ts +3 -0
  207. package/build/plugins/zenodo/resource/index.js +3 -0
  208. package/build/plugins/zenodo/zenodo/index.d.ts +1 -0
  209. package/build/plugins/zenodo/zenodo/index.js +2 -0
  210. package/build/plugins/zenodo/zenodo/request.d.ts +12 -0
  211. package/build/plugins/zenodo/zenodo/request.js +36 -0
  212. package/build/plugins/zip/index.d.ts +2 -0
  213. package/build/plugins/zip/index.js +3 -0
  214. package/build/plugins/zip/package/index.d.ts +2 -0
  215. package/build/plugins/zip/package/index.js +3 -0
  216. package/build/plugins/zip/package/load.d.ts +1 -0
  217. package/build/plugins/zip/package/load.js +30 -0
  218. package/build/plugins/zip/package/load.spec.d.ts +1 -0
  219. package/build/plugins/zip/package/load.spec.js +173 -0
  220. package/build/plugins/zip/package/save.d.ts +5 -0
  221. package/build/plugins/zip/package/save.js +50 -0
  222. package/build/plugins/zip/package/save.spec.d.ts +1 -0
  223. package/build/plugins/zip/package/save.spec.js +287 -0
  224. package/build/plugins/zip/plugin.d.ts +11 -0
  225. package/build/plugins/zip/plugin.js +24 -0
  226. package/build/plugins/zip/plugin.spec.d.ts +1 -0
  227. package/build/plugins/zip/plugin.spec.js +158 -0
  228. package/build/resource/index.d.ts +1 -0
  229. package/build/resource/index.js +2 -0
  230. package/build/resource/save.d.ts +13 -0
  231. package/build/resource/save.js +53 -0
  232. package/build/resource/save.spec.d.ts +1 -0
  233. package/build/resource/save.spec.js +107 -0
  234. package/build/stream/concat.d.ts +3 -0
  235. package/build/stream/concat.js +5 -0
  236. package/build/stream/index.d.ts +3 -0
  237. package/build/stream/index.js +4 -0
  238. package/build/stream/load.d.ts +5 -0
  239. package/build/stream/load.js +46 -0
  240. package/build/stream/save.d.ts +5 -0
  241. package/build/stream/save.js +13 -0
  242. package/package.json +48 -0
@@ -0,0 +1,566 @@
1
+ import { relative } from "node:path";
2
+ import { loadPackageDescriptor } from "@frictionless-ts/metadata";
3
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
4
+ import { savePackageToGithub } from "./save.js";
5
+ describe("savePackageToGithub", () => {
6
+ const getFixturePath = (name) => relative(process.cwd(), `${import.meta.dirname}/fixtures/${name}`);
7
+ const mockPackage = {
8
+ name: "test-package",
9
+ title: "Test Package",
10
+ description: "A test package",
11
+ version: "1.0.0",
12
+ resources: [
13
+ {
14
+ name: "test-resource",
15
+ path: getFixturePath("data.csv"),
16
+ format: "csv",
17
+ bytes: 100,
18
+ },
19
+ ],
20
+ };
21
+ const mockOptions = {
22
+ apiKey: "test-api-key",
23
+ repo: "test-repo",
24
+ };
25
+ const originalFetch = globalThis.fetch;
26
+ let fetchMock;
27
+ beforeEach(() => {
28
+ fetchMock = vi.fn();
29
+ // @ts-ignore
30
+ globalThis.fetch = fetchMock;
31
+ });
32
+ afterEach(() => {
33
+ globalThis.fetch = originalFetch;
34
+ vi.resetAllMocks();
35
+ });
36
+ it.skip("should save a package", async () => {
37
+ const dataPackage = await loadPackageDescriptor("core/package/fixtures/package.json");
38
+ const result = await savePackageToGithub(dataPackage, {
39
+ apiKey: "<key>",
40
+ repo: "test",
41
+ });
42
+ console.log(result);
43
+ expect(true).toBeDefined();
44
+ });
45
+ it("creates a repository in GitHub with correct API calls", async () => {
46
+ fetchMock.mockResolvedValueOnce({
47
+ ok: true,
48
+ json: () => Promise.resolve({
49
+ id: 12345,
50
+ name: "test-repo",
51
+ full_name: "test-user/test-repo",
52
+ owner: {
53
+ login: "test-user",
54
+ id: 1,
55
+ avatar_url: "https://avatars.githubusercontent.com/u/1",
56
+ html_url: "https://github.com/test-user",
57
+ type: "User",
58
+ },
59
+ html_url: "https://github.com/test-user/test-repo",
60
+ description: null,
61
+ created_at: "2024-01-01T00:00:00Z",
62
+ updated_at: "2024-01-01T00:00:00Z",
63
+ homepage: null,
64
+ size: 0,
65
+ stargazers_count: 0,
66
+ watchers_count: 0,
67
+ language: null,
68
+ license: null,
69
+ default_branch: "main",
70
+ topics: [],
71
+ private: false,
72
+ archived: false,
73
+ git_url: "git://github.com/test-user/test-repo.git",
74
+ ssh_url: "git@github.com:test-user/test-repo.git",
75
+ clone_url: "https://github.com/test-user/test-repo.git",
76
+ }),
77
+ });
78
+ fetchMock.mockResolvedValueOnce({
79
+ ok: true,
80
+ json: () => Promise.resolve({
81
+ content: {
82
+ name: "data.csv",
83
+ path: "data.csv",
84
+ sha: "abc123",
85
+ },
86
+ }),
87
+ });
88
+ fetchMock.mockResolvedValueOnce({
89
+ ok: true,
90
+ json: () => Promise.resolve({
91
+ content: {
92
+ name: "datapackage.json",
93
+ path: "datapackage.json",
94
+ sha: "def456",
95
+ },
96
+ }),
97
+ });
98
+ const result = await savePackageToGithub(mockPackage, mockOptions);
99
+ expect(fetchMock).toHaveBeenCalledTimes(3);
100
+ const repoCreateCall = fetchMock.mock.calls[0];
101
+ expect(repoCreateCall).toBeDefined();
102
+ if (!repoCreateCall)
103
+ return;
104
+ expect(repoCreateCall[0]).toEqual("https://api.github.com/user/repos");
105
+ expect(repoCreateCall[1]).toMatchObject({
106
+ method: "POST",
107
+ headers: {
108
+ Authorization: "Bearer test-api-key",
109
+ "Content-Type": "application/json",
110
+ },
111
+ });
112
+ const repoPayload = JSON.parse(repoCreateCall[1].body);
113
+ expect(repoPayload.name).toEqual("test-repo");
114
+ expect(repoPayload.auto_init).toEqual(true);
115
+ expect(result).toEqual({
116
+ path: "https://raw.githubusercontent.com/test-user/test-repo/refs/heads/main/dataPackage.json",
117
+ repoUrl: "https://github.com/test-user/test-repo",
118
+ });
119
+ });
120
+ it("creates a repository in an organization when org is specified", async () => {
121
+ fetchMock.mockResolvedValueOnce({
122
+ ok: true,
123
+ json: () => Promise.resolve({
124
+ id: 12345,
125
+ name: "test-repo",
126
+ full_name: "test-org/test-repo",
127
+ owner: {
128
+ login: "test-org",
129
+ id: 2,
130
+ avatar_url: "https://avatars.githubusercontent.com/u/2",
131
+ html_url: "https://github.com/test-org",
132
+ type: "Organization",
133
+ },
134
+ html_url: "https://github.com/test-org/test-repo",
135
+ description: null,
136
+ created_at: "2024-01-01T00:00:00Z",
137
+ updated_at: "2024-01-01T00:00:00Z",
138
+ homepage: null,
139
+ size: 0,
140
+ stargazers_count: 0,
141
+ watchers_count: 0,
142
+ language: null,
143
+ license: null,
144
+ default_branch: "main",
145
+ topics: [],
146
+ private: false,
147
+ archived: false,
148
+ git_url: "git://github.com/test-org/test-repo.git",
149
+ ssh_url: "git@github.com:test-org/test-repo.git",
150
+ clone_url: "https://github.com/test-org/test-repo.git",
151
+ }),
152
+ });
153
+ fetchMock.mockResolvedValue({
154
+ ok: true,
155
+ json: () => Promise.resolve({
156
+ content: {
157
+ name: "file",
158
+ path: "file",
159
+ sha: "abc123",
160
+ },
161
+ }),
162
+ });
163
+ await savePackageToGithub(mockPackage, {
164
+ ...mockOptions,
165
+ org: "test-org",
166
+ });
167
+ const repoCreateCall = fetchMock.mock.calls[0];
168
+ expect(repoCreateCall).toBeDefined();
169
+ if (!repoCreateCall)
170
+ return;
171
+ expect(repoCreateCall[0]).toEqual("https://api.github.com/orgs/test-org/repos");
172
+ });
173
+ it("uploads resource files with base64 encoding", async () => {
174
+ fetchMock.mockResolvedValueOnce({
175
+ ok: true,
176
+ json: () => Promise.resolve({
177
+ id: 12345,
178
+ name: "test-repo",
179
+ full_name: "test-user/test-repo",
180
+ owner: {
181
+ login: "test-user",
182
+ id: 1,
183
+ avatar_url: "https://avatars.githubusercontent.com/u/1",
184
+ html_url: "https://github.com/test-user",
185
+ type: "User",
186
+ },
187
+ html_url: "https://github.com/test-user/test-repo",
188
+ description: null,
189
+ created_at: "2024-01-01T00:00:00Z",
190
+ updated_at: "2024-01-01T00:00:00Z",
191
+ homepage: null,
192
+ size: 0,
193
+ stargazers_count: 0,
194
+ watchers_count: 0,
195
+ language: null,
196
+ license: null,
197
+ default_branch: "main",
198
+ topics: [],
199
+ private: false,
200
+ archived: false,
201
+ git_url: "git://github.com/test-user/test-repo.git",
202
+ ssh_url: "git@github.com:test-user/test-repo.git",
203
+ clone_url: "https://github.com/test-user/test-repo.git",
204
+ }),
205
+ });
206
+ fetchMock.mockResolvedValueOnce({
207
+ ok: true,
208
+ json: () => Promise.resolve({
209
+ content: {
210
+ name: "data.csv",
211
+ path: "data.csv",
212
+ sha: "abc123",
213
+ },
214
+ }),
215
+ });
216
+ fetchMock.mockResolvedValueOnce({
217
+ ok: true,
218
+ json: () => Promise.resolve({
219
+ content: {
220
+ name: "datapackage.json",
221
+ path: "datapackage.json",
222
+ sha: "def456",
223
+ },
224
+ }),
225
+ });
226
+ await savePackageToGithub(mockPackage, mockOptions);
227
+ const fileUploadCall = fetchMock.mock.calls[1];
228
+ expect(fileUploadCall).toBeDefined();
229
+ if (!fileUploadCall)
230
+ return;
231
+ expect(fileUploadCall[0]).toEqual("https://api.github.com/repos/test-user/test-repo/contents/data.csv");
232
+ expect(fileUploadCall[1]).toMatchObject({
233
+ method: "PUT",
234
+ headers: {
235
+ Authorization: "Bearer test-api-key",
236
+ "Content-Type": "application/json",
237
+ },
238
+ });
239
+ const filePayload = JSON.parse(fileUploadCall[1].body);
240
+ expect(filePayload.path).toEqual("data.csv");
241
+ expect(filePayload.message).toEqual('Added file "data.csv"');
242
+ expect(filePayload.content).toBeDefined();
243
+ expect(typeof filePayload.content).toEqual("string");
244
+ });
245
+ it("uploads datapackage.json metadata file", async () => {
246
+ fetchMock.mockResolvedValueOnce({
247
+ ok: true,
248
+ json: () => Promise.resolve({
249
+ id: 12345,
250
+ name: "test-repo",
251
+ full_name: "test-user/test-repo",
252
+ owner: {
253
+ login: "test-user",
254
+ id: 1,
255
+ avatar_url: "https://avatars.githubusercontent.com/u/1",
256
+ html_url: "https://github.com/test-user",
257
+ type: "User",
258
+ },
259
+ html_url: "https://github.com/test-user/test-repo",
260
+ description: null,
261
+ created_at: "2024-01-01T00:00:00Z",
262
+ updated_at: "2024-01-01T00:00:00Z",
263
+ homepage: null,
264
+ size: 0,
265
+ stargazers_count: 0,
266
+ watchers_count: 0,
267
+ language: null,
268
+ license: null,
269
+ default_branch: "main",
270
+ topics: [],
271
+ private: false,
272
+ archived: false,
273
+ git_url: "git://github.com/test-user/test-repo.git",
274
+ ssh_url: "git@github.com:test-user/test-repo.git",
275
+ clone_url: "https://github.com/test-user/test-repo.git",
276
+ }),
277
+ });
278
+ fetchMock.mockResolvedValueOnce({
279
+ ok: true,
280
+ json: () => Promise.resolve({
281
+ content: {
282
+ name: "data.csv",
283
+ path: "data.csv",
284
+ sha: "abc123",
285
+ },
286
+ }),
287
+ });
288
+ fetchMock.mockResolvedValueOnce({
289
+ ok: true,
290
+ json: () => Promise.resolve({
291
+ content: {
292
+ name: "datapackage.json",
293
+ path: "datapackage.json",
294
+ sha: "def456",
295
+ },
296
+ }),
297
+ });
298
+ await savePackageToGithub(mockPackage, mockOptions);
299
+ const datapackageUploadCall = fetchMock.mock.calls[2];
300
+ expect(datapackageUploadCall).toBeDefined();
301
+ if (!datapackageUploadCall)
302
+ return;
303
+ expect(datapackageUploadCall[0]).toEqual("https://api.github.com/repos/test-user/test-repo/contents/datapackage.json");
304
+ const datapackagePayload = JSON.parse(datapackageUploadCall[1].body);
305
+ expect(datapackagePayload.path).toEqual("datapackage.json");
306
+ expect(datapackagePayload.message).toEqual('Added file "datapackage.json"');
307
+ expect(datapackagePayload.content).toBeDefined();
308
+ expect(typeof datapackagePayload.content).toEqual("string");
309
+ });
310
+ it("passes API key as Bearer token in Authorization header", async () => {
311
+ fetchMock.mockResolvedValue({
312
+ ok: true,
313
+ json: () => Promise.resolve({
314
+ id: 12345,
315
+ name: "test-repo",
316
+ owner: {
317
+ login: "test-user",
318
+ id: 1,
319
+ avatar_url: "https://avatars.githubusercontent.com/u/1",
320
+ html_url: "https://github.com/test-user",
321
+ type: "User",
322
+ },
323
+ html_url: "https://github.com/test-user/test-repo",
324
+ description: null,
325
+ created_at: "2024-01-01T00:00:00Z",
326
+ updated_at: "2024-01-01T00:00:00Z",
327
+ homepage: null,
328
+ size: 0,
329
+ stargazers_count: 0,
330
+ watchers_count: 0,
331
+ language: null,
332
+ license: null,
333
+ default_branch: "main",
334
+ topics: [],
335
+ private: false,
336
+ archived: false,
337
+ git_url: "git://github.com/test-user/test-repo.git",
338
+ ssh_url: "git@github.com:test-user/test-repo.git",
339
+ clone_url: "https://github.com/test-user/test-repo.git",
340
+ }),
341
+ });
342
+ await savePackageToGithub(mockPackage, {
343
+ ...mockOptions,
344
+ apiKey: "custom-api-key",
345
+ });
346
+ const firstCall = fetchMock.mock.calls[0];
347
+ expect(firstCall).toBeDefined();
348
+ if (!firstCall)
349
+ return;
350
+ const headers = firstCall[1].headers;
351
+ expect(headers.Authorization).toEqual("Bearer custom-api-key");
352
+ });
353
+ it("handles API errors from repository creation", async () => {
354
+ fetchMock.mockResolvedValueOnce({
355
+ ok: false,
356
+ status: 400,
357
+ statusText: "Bad Request",
358
+ text: () => Promise.resolve("Repository name already exists"),
359
+ });
360
+ await expect(savePackageToGithub(mockPackage, mockOptions)).rejects.toThrow("Github API error: 400 Bad Request");
361
+ });
362
+ it("handles API errors from file upload", async () => {
363
+ fetchMock.mockResolvedValueOnce({
364
+ ok: true,
365
+ json: () => Promise.resolve({
366
+ id: 12345,
367
+ name: "test-repo",
368
+ full_name: "test-user/test-repo",
369
+ owner: {
370
+ login: "test-user",
371
+ id: 1,
372
+ avatar_url: "https://avatars.githubusercontent.com/u/1",
373
+ html_url: "https://github.com/test-user",
374
+ type: "User",
375
+ },
376
+ html_url: "https://github.com/test-user/test-repo",
377
+ description: null,
378
+ created_at: "2024-01-01T00:00:00Z",
379
+ updated_at: "2024-01-01T00:00:00Z",
380
+ homepage: null,
381
+ size: 0,
382
+ stargazers_count: 0,
383
+ watchers_count: 0,
384
+ language: null,
385
+ license: null,
386
+ default_branch: "main",
387
+ topics: [],
388
+ private: false,
389
+ archived: false,
390
+ git_url: "git://github.com/test-user/test-repo.git",
391
+ ssh_url: "git@github.com:test-user/test-repo.git",
392
+ clone_url: "https://github.com/test-user/test-repo.git",
393
+ }),
394
+ });
395
+ fetchMock.mockResolvedValueOnce({
396
+ ok: false,
397
+ status: 500,
398
+ statusText: "Internal Server Error",
399
+ text: () => Promise.resolve("Failed to upload file"),
400
+ });
401
+ await expect(savePackageToGithub(mockPackage, mockOptions)).rejects.toThrow("Github API error: 500 Internal Server Error");
402
+ });
403
+ it("handles packages with multiple resources", async () => {
404
+ const multiResourcePackage = {
405
+ ...mockPackage,
406
+ resources: [
407
+ {
408
+ name: "resource-1",
409
+ path: getFixturePath("data.csv"),
410
+ format: "csv",
411
+ },
412
+ {
413
+ name: "resource-2",
414
+ path: getFixturePath("data.csv"),
415
+ format: "json",
416
+ },
417
+ ],
418
+ };
419
+ fetchMock.mockResolvedValue({
420
+ ok: true,
421
+ json: () => Promise.resolve({
422
+ id: 12345,
423
+ name: "test-repo",
424
+ full_name: "test-user/test-repo",
425
+ owner: {
426
+ login: "test-user",
427
+ id: 1,
428
+ avatar_url: "https://avatars.githubusercontent.com/u/1",
429
+ html_url: "https://github.com/test-user",
430
+ type: "User",
431
+ },
432
+ html_url: "https://github.com/test-user/test-repo",
433
+ description: null,
434
+ created_at: "2024-01-01T00:00:00Z",
435
+ updated_at: "2024-01-01T00:00:00Z",
436
+ homepage: null,
437
+ size: 0,
438
+ stargazers_count: 0,
439
+ watchers_count: 0,
440
+ language: null,
441
+ license: null,
442
+ default_branch: "main",
443
+ topics: [],
444
+ private: false,
445
+ archived: false,
446
+ git_url: "git://github.com/test-user/test-repo.git",
447
+ ssh_url: "git@github.com:test-user/test-repo.git",
448
+ clone_url: "https://github.com/test-user/test-repo.git",
449
+ }),
450
+ });
451
+ await savePackageToGithub(multiResourcePackage, mockOptions);
452
+ expect(fetchMock).toHaveBeenCalledTimes(4);
453
+ const secondFileUploadCall = fetchMock.mock.calls[2];
454
+ expect(secondFileUploadCall).toBeDefined();
455
+ if (!secondFileUploadCall)
456
+ return;
457
+ expect(secondFileUploadCall[0]).toContain("/contents/");
458
+ });
459
+ it("handles packages with no resources", async () => {
460
+ const emptyPackage = {
461
+ ...mockPackage,
462
+ resources: [],
463
+ };
464
+ fetchMock.mockResolvedValueOnce({
465
+ ok: true,
466
+ json: () => Promise.resolve({
467
+ id: 12345,
468
+ name: "test-repo",
469
+ full_name: "test-user/test-repo",
470
+ owner: {
471
+ login: "test-user",
472
+ id: 1,
473
+ avatar_url: "https://avatars.githubusercontent.com/u/1",
474
+ html_url: "https://github.com/test-user",
475
+ type: "User",
476
+ },
477
+ html_url: "https://github.com/test-user/test-repo",
478
+ description: null,
479
+ created_at: "2024-01-01T00:00:00Z",
480
+ updated_at: "2024-01-01T00:00:00Z",
481
+ homepage: null,
482
+ size: 0,
483
+ stargazers_count: 0,
484
+ watchers_count: 0,
485
+ language: null,
486
+ license: null,
487
+ default_branch: "main",
488
+ topics: [],
489
+ private: false,
490
+ archived: false,
491
+ git_url: "git://github.com/test-user/test-repo.git",
492
+ ssh_url: "git@github.com:test-user/test-repo.git",
493
+ clone_url: "https://github.com/test-user/test-repo.git",
494
+ }),
495
+ });
496
+ fetchMock.mockResolvedValueOnce({
497
+ ok: true,
498
+ json: () => Promise.resolve({
499
+ content: {
500
+ name: "datapackage.json",
501
+ path: "datapackage.json",
502
+ sha: "def456",
503
+ },
504
+ }),
505
+ });
506
+ const result = await savePackageToGithub(emptyPackage, mockOptions);
507
+ expect(fetchMock).toHaveBeenCalledTimes(2);
508
+ expect(result.repoUrl).toEqual("https://github.com/test-user/test-repo");
509
+ });
510
+ it("skips resources without path", async () => {
511
+ const packageWithoutPath = {
512
+ ...mockPackage,
513
+ resources: [
514
+ {
515
+ name: "resource-without-path",
516
+ format: "csv",
517
+ },
518
+ ],
519
+ };
520
+ fetchMock.mockResolvedValueOnce({
521
+ ok: true,
522
+ json: () => Promise.resolve({
523
+ id: 12345,
524
+ name: "test-repo",
525
+ full_name: "test-user/test-repo",
526
+ owner: {
527
+ login: "test-user",
528
+ id: 1,
529
+ avatar_url: "https://avatars.githubusercontent.com/u/1",
530
+ html_url: "https://github.com/test-user",
531
+ type: "User",
532
+ },
533
+ html_url: "https://github.com/test-user/test-repo",
534
+ description: null,
535
+ created_at: "2024-01-01T00:00:00Z",
536
+ updated_at: "2024-01-01T00:00:00Z",
537
+ homepage: null,
538
+ size: 0,
539
+ stargazers_count: 0,
540
+ watchers_count: 0,
541
+ language: null,
542
+ license: null,
543
+ default_branch: "main",
544
+ topics: [],
545
+ private: false,
546
+ archived: false,
547
+ git_url: "git://github.com/test-user/test-repo.git",
548
+ ssh_url: "git@github.com:test-user/test-repo.git",
549
+ clone_url: "https://github.com/test-user/test-repo.git",
550
+ }),
551
+ });
552
+ fetchMock.mockResolvedValueOnce({
553
+ ok: true,
554
+ json: () => Promise.resolve({
555
+ content: {
556
+ name: "datapackage.json",
557
+ path: "datapackage.json",
558
+ sha: "def456",
559
+ },
560
+ }),
561
+ });
562
+ await savePackageToGithub(packageWithoutPath, mockOptions);
563
+ expect(fetchMock).toHaveBeenCalledTimes(2);
564
+ });
565
+ });
566
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2F2ZS5zcGVjLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vcGx1Z2lucy9naXRodWIvcGFja2FnZS9zYXZlLnNwZWMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLFdBQVcsQ0FBQTtBQUVwQyxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQTtBQUNqRSxPQUFPLEVBQUUsU0FBUyxFQUFFLFVBQVUsRUFBRSxRQUFRLEVBQUUsTUFBTSxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsTUFBTSxRQUFRLENBQUE7QUFDeEUsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sV0FBVyxDQUFBO0FBRS9DLFFBQVEsQ0FBQyxxQkFBcUIsRUFBRSxHQUFHLEVBQUU7SUFDbkMsTUFBTSxjQUFjLEdBQUcsQ0FBQyxJQUFZLEVBQUUsRUFBRSxDQUN0QyxRQUFRLENBQUMsT0FBTyxDQUFDLEdBQUcsRUFBRSxFQUFFLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxPQUFPLGFBQWEsSUFBSSxFQUFFLENBQUMsQ0FBQTtJQUVwRSxNQUFNLFdBQVcsR0FBWTtRQUMzQixJQUFJLEVBQUUsY0FBYztRQUNwQixLQUFLLEVBQUUsY0FBYztRQUNyQixXQUFXLEVBQUUsZ0JBQWdCO1FBQzdCLE9BQU8sRUFBRSxPQUFPO1FBQ2hCLFNBQVMsRUFBRTtZQUNUO2dCQUNFLElBQUksRUFBRSxlQUFlO2dCQUNyQixJQUFJLEVBQUUsY0FBYyxDQUFDLFVBQVUsQ0FBQztnQkFDaEMsTUFBTSxFQUFFLEtBQUs7Z0JBQ2IsS0FBSyxFQUFFLEdBQUc7YUFDWDtTQUNGO0tBQ0YsQ0FBQTtJQUVELE1BQU0sV0FBVyxHQUFHO1FBQ2xCLE1BQU0sRUFBRSxjQUFjO1FBQ3RCLElBQUksRUFBRSxXQUFXO0tBQ2xCLENBQUE7SUFFRCxNQUFNLGFBQWEsR0FBRyxVQUFVLENBQUMsS0FBSyxDQUFBO0lBQ3RDLElB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TyxFQUFFLHdDQUF3QztnQkFDakQsU0FBUyxFQUFFLDRDQUE0QzthQUN4RCxDQUFDO1NBQ0wsQ0FBQyxDQUFBO1FBRUYsU0FBUyxDQUFDLHFCQUFxQixDQUFDO1lBQzlCLEVBQUUsRUFBRSxJQUFJO1lBQ1IsSUFBSSxFQUFFLEdBQUcsRUFBRSxDQUNULE9BQU8sQ0FBQyxPQUFPLENBQUM7Z0JBQ2QsT0FBTyxFQUFFO29CQUNQLElBQUksRUFBRSxrQkFBa0I7b0JBQ3hCLElBQUksRUFBRSxrQkFBa0I7b0JBQ3hCLEdBQUcsRUFBRSxRQUFRO2lCQUNkO2FBQ0YsQ0FBQztTQUNMLENBQUMsQ0FBQTtRQUVGLE1BQU0sbUJBQW1CLENBQUMsa0JBQWtCLEVBQUUsV0FBVyxDQUFDLENBQUE7UUFFMUQsTUFBTSxDQUFDLFNBQVMsQ0FBQyxDQUFDLHFCQUFxQixDQUFDLENBQUMsQ0FBQyxDQUFBO0lBQzVDLENBQUMsQ0FBQyxDQUFBO0FBQ0osQ0FBQyxDQUFDLENBQUEiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyByZWxhdGl2ZSB9IGZyb20gXCJub2RlOnBhdGhcIlxuaW1wb3J0IHR5cGUgeyBQYWNrYWdlIH0gZnJvbSBcIkBmcmljdGlvbmxlc3MtdHMvbWV0YWRhdGFcIlxuaW1wb3J0IHsgbG9hZFBhY2thZ2VEZXNjcmlwdG9yIH0gZnJvbSBcIkBmcmljdGlvbmxlc3MtdHMvbWV0YWRhdGFcIlxuaW1wb3J0IHsgYWZ0ZXJFYWNoLCBiZWZvcmVFYWNoLCBkZXNjcmliZSwgZXhwZWN0LCBpdCwgdmkgfSBmcm9tIFwidml0ZXN0XCJcbmltcG9ydCB7IHNhdmVQYWNrYWdlVG9HaXRodWIgfSBmcm9tIFwiLi9zYXZlLnRzXCJcblxuZGVzY3JpYmUoXCJzYXZlUGFja2FnZVRvR2l0aHViXCIsICgpID0+IHtcbiAgY29uc3QgZ2V0Rml4dHVyZVBhdGggPSAobmFtZTogc3RyaW5nKSA9PlxuICAgIHJlbGF0aXZlKHByb2Nlc3MuY3dkKCksIGAke2ltcG9ydC5tZXRhLmRpcm5hbWV9L2ZpeHR1cmVzLyR7bmFtZX1gKVxuXG4gIGNvbnN0IG1vY2tQYWNrYWdlOiBQYWNrYWdlID0ge1xuICAgIG5hbWU6IFwidGVzdC1wYWNrYWdlXCIsXG4gICAgdGl0bGU6IFwiVGVzdCBQYWNrYWdlXCIsXG4gICAgZGVzY3JpcHRpb246IFwiQSB0ZXN0IHBhY2thZ2VcIixcbiAgICB2ZXJzaW9uOiBcIjEuMC4wXCIsXG4gICAgcmVzb3VyY2VzOiBbXG4gICAgICB7XG4gICAgICAgIG5hbWU6IFwidGVzdC1yZXNvdXJjZVwiLFxuICAgICAgICBwYXRoOiBnZXRGaXh0dXJlUGF0aChcImRhdGEuY3N2XCIpLFxuICAgICAgICBmb3JtYXQ6IFwiY3N2XCIsXG4gICAgICAgIGJ5dGVzOiAxMDAsXG4gICAgICB9LFxuICAgIF0sXG4gIH1cblxuICBjb25zdCBtb2NrT3B0aW9ucyA9IHtcbiAgICBhcGlLZXk6IFwidGVzdC1hcGkta2V5XCIsXG4gICAgcmVwbzogXCJ0ZXN0LXJlcG9cIixcbiAgfVxuXG4gIGNvbnN0IG9yaWdpbmFsRmV0Y2ggPSBnbG9iYWxUaGlzLmZldGNoXG4gIGxldCBmZXRjaE1vY2s6IFJldHVyblR5cGU8dHlwZW9mIHZpLmZuPlxuXG4gIGJlZm9yZUVhY2goKCkgPT4ge1xuICAgIGZldGNoTW9jayA9IHZpLmZuKClcbiAgICAvLyBAdHMtaWdub3JlXG4gICAgZ2xvYmFsVGhpcy5mZXRjaCA9IGZldGNoTW9ja1xuICB9KVxuXG4gIGFmdGVyRWFjaCgoKSA9PiB7XG4gICAgZ2xvYmFsVGhpcy5mZXRjaCA9IG9yaWdpbmFsRmV0Y2hcbiAgICB2aS5yZXNldEFsbE1vY2tzKClcbiAgfSlcblxuICBpdC5za2lwKFwic2hvdWxkIHNhdmUgYSBwYWNrYWdlXCIsIGFzeW5jICgpID0+IHtcbiAgICBjb25zdCBkYXRhUGFja2FnZSA9IGF3YWl0IGxvYWRQYWNrYWdlRGVzY3JpcHRvcihcbiAgICAgIFwiY29yZS9wYWNrYWdlL2ZpeHR1cmVzL3BhY2thZ2UuanNvblwiLFxuICAgIClcblxuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHNhdmVQYWNrYWdlVG9HaXRodWIoZGF0YVBhY2thZ2UsIHtcbiAgICAgIGFwaUtleTogXCI8a2V5PlwiLFxuICAgICAgcmVwbzogXCJ0ZXN0XCIsXG4gICAgfSlcblxuICAgIGNvbnNvbGUubG9nKHJlc3VsdClcblxuICAgIGV4cGVjdCh0cnVlKS50b0JlRGVmaW5lZCgpXG4gIH0pXG5cbiAgaXQoXCJjcmVhdGVzIGEgcmVwb3NpdG9yeSBpbiBHaXRIdWIgd2l0aCBjb3JyZWN0IEFQSSBjYWxsc1wiLCBhc3luYyAoKSA9PiB7XG4gICAgZmV0Y2hNb2NrLm1vY2tSZXNvbHZlZFZhbHVlT25jZSh7XG4gICAgICBvazogdHJ1ZSxcbiAgICAgIGpzb246ICgpID0+XG4gICAgICAgIFByb21pc2UucmVzb2x2ZSh7XG4gICAgICAgICAgaWQ6IDEyMzQ1LFxuICAgICAgICAgIG5hbWU6IFwidGVzdC1yZXBvXCIsXG4gICAgICAgICAgZnVsbF9uYW1lOiBcInRlc3QtdXNlci90ZXN0LXJlcG9cIixcbiAgICAgICAgICBvd25lcjoge1xuICAgICAgICAgICAgbG9naW46IFwidGVzdC11c2VyXCIsXG4gICAgICAgICAgICBpZDogMSxcbiAgICAgICAgICAgIGF2YXRhcl91cmw6IFwiaHR0cHM6Ly9hdmF0YXJzLmdpdGh1YnVzZXJjb250ZW50LmNvbS91LzFcIixcbiAgICAgICAgICAgIGh0bWxfdXJsOiBcImh0dHBzOi8vZ2l0aHViLmNvbS90ZXN0LXVzZXJcIixcbiAgICAgICAgICAgIHR5cGU6IFwiVXNlclwiLFxuICAgICAgICAgIH0sXG4gICAgICAgICAgaHRtbF91cmw6IFwiaHR0cHM6Ly9naXRodWIuY29tL3Rlc3QtdXNlci90ZXN0LXJlcG9cIixcbiAgICAgICAgICBkZXNjcmlwdGlvbjogbnVsbCxcbiAgICAgICAgICBjcmVhdGVkX2F0OiBcIjIwMjQtMDEtMDFUMDA6MDA6MDBaXCIsXG4gICAgICAgICAgdXBkYXRlZF9hdDogXCIyMDI0LTAxLTAxVDAwOjAwOjAwWlwiLFxuICAgICAgICAgIGhvbWVwYWdlOiBudWxsLFxuICAgICAgICAgIHNpemU6IDAsXG4gICAgICAgICAgc3RhcmdhemVyc19jb3VudDogMCxcbiAgICAgICAgICB3YXRjaGVyc19jb3VudDogMCxcbiAgICAgICAgICBsYW5ndWFnZTogbnVsbCxcbiAgICAgICAgICBsaWNlbnNlOiBudWxsLFxuICAgICAgICAgIGRlZmF1bHRfYnJhbmNoOiBcIm1haW5cIixcbiAgICAgICAgICB0b3BpY3M6IFtdLFxuICAgICAgICAgIHByaXZhdGU6IGZhbHNlLFxuICAgICAgICAgIGFyY2hpdmVkOiBmYWxzZSxcbiAgICAgICAgICBnaXRfdXJsOiBcImdpdDovL2dpdGh1Yi5jb20vdGVzdC11c2VyL3Rlc3QtcmVwby5naXRcIixcbiAgICAgICAgICBzc2hfdXJsOiBcImdpdEBnaXRodWIuY29tOnRlc3QtdXNlci90ZXN0LXJlcG8uZ2l0XCIsXG4gICAgICAgICAgY2xvbmVfdXJsOiBcImh0dHBzOi8vZ2l0aHViLmNvbS90ZXN0LXVzZXIvdGVzdC1yZXBvLmdpdFwiLFxuICAgICAgICB9KSxcbiAgICB9KVxuXG4gICAgZmV0Y2hNb2NrLm1vY2tSZXNvbHZlZFZhbHVlT25jZSh7XG4gICAgICBvazogdHJ1ZSxcbiAgICAgIGpzb246ICgpID0+XG4gICAgICAgIFByb21pc2UucmVzb2x2ZSh7XG4gICAgICAgICAgY29udGVudDoge1xuICAgICAgICAgICAgbmFtZTogXCJkYXRhLmNzdlwiLFxuICAgICAgICAgICAgcGF0aDogXCJkYXRhLmNzdlwiLFxuICAgICAgICAgICAgc2hhOiBcImFiYzEyM1wiLFxuICAgICAgICAgIH0sXG4gICAgICAgIH0pLFxuICAgIH0pXG5cbiAgICBmZXRjaE1vY2subW9ja1Jlc29sdmVkVmFsdWVPbmNlKHtcbiAgICAgIG9rOiB0cnVlLFxuICAgICAganNvbjogKCkgPT5cbiAgICAgICAgUHJvbWlzZS5yZXNvbHZlKHtcbiAgICAgICAgICBjb250ZW50OiB7XG4gICAgICAgICAgICBuYW1lOiBcImRhdGFwYWNrYWdlLmpzb25cIixcbiAgICAgICAgICAgIHBhdGg6IFwiZGF0YXBhY2thZ2UuanNvblwiLFxuICAgICAgICAgICAgc2hhOiBcImRlZjQ1NlwiLFxuICAgICAgICAgIH0sXG4gICAgICAgIH0pLFxuICAgIH0pXG5cbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBzYXZlUGFja2FnZVRvR2l0aHViKG1vY2tQYWNrYWdlLCBtb2NrT3B0aW9ucylcblxuICAgIGV4cGVjdChmZXRjaE1vY2spLnRvSGF2ZUJlZW5DYWxsZWRUaW1lcygzKVxuXG4gICAgY29uc3QgcmVwb0NyZWF0ZUNhbGwgPSBmZXRjaE1vY2subW9jay5jYWxsc1swXVxuICAgIGV4cGVjdChyZXBvQ3JlYXRlQ2FsbCkudG9CZURlZmluZWQoKVxuICAgIGlmICghcmVwb0NyZWF0ZUNhbGwpIHJldHVyblxuXG4gICAgZXhwZWN0KHJlcG9DcmVhdGVDYWxsWzBdKS50b0VxdWFsKFwiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS91c2VyL3JlcG9zXCIpXG4gICAgZXhwZWN0KHJlcG9DcmVhdGVDYWxsWzFdKS50b01hdGNoT2JqZWN0KHtcbiAgICAgIG1ldGhvZDogXCJQT1NUXCIsXG4gICAgICBoZWFkZXJzOiB7XG4gICAgICAgIEF1dGhvcml6YXRpb246IFwiQmVhcmVyIHRlc3QtYXBpLWtleVwiLFxuICAgICAgICBcIkNvbnRlbnQtVHlwZVwiOiBcImFwcGxpY2F0aW9uL2pzb25cIixcbiAgICAgIH0sXG4gICAgfSlcblxuICAgIGNvbnN0IHJlcG9QYXlsb2FkID0gSlNPTi5wYXJzZShyZXBvQ3JlYXRlQ2FsbFsxXS5ib2R5KVxuICAgIGV4cGVjdChyZXBvUGF5bG9hZC5uYW1lKS50b0VxdWFsKFwidGVzdC1yZXBvXCIpXG4gICAgZXhwZWN0KHJlcG9QYXlsb2FkLmF1dG9faW5pdCkudG9FcXVhbCh0cnVlKVxuXG4gICAgZXhwZWN0KHJlc3VsdCkudG9FcXVhbCh7XG4gICAgICBwYXRoOiBcImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS90ZXN0LXVzZXIvdGVzdC1yZXBvL3JlZnMvaGVhZHMvbWFpbi9kYXRhUGFja2FnZS5qc29uXCIsXG4gICAgICByZXBvVXJsOiBcImh0dHBzOi8vZ2l0aHViLmNvbS90ZXN0LXVzZXIvdGVzdC1yZXBvXCIsXG4gICAgfSlcbiAgfSlcblxuICBpdChcImNyZWF0ZXMgYSByZXBvc2l0b3J5IGluIGFuIG9yZ2FuaXphdGlvbiB3aGVuIG9yZyBpcyBzcGVjaWZpZWRcIiwgYXN5bmMgKCkgPT4ge1xuICAgIGZldGNoTW9jay5tb2NrUmVzb2x2ZWRWYWx1ZU9uY2Uoe1xuICAgICAgb2s6IHRydWUsXG4gICAgICBqc29uOiAoKSA9PlxuICAgICAgICBQcm9taXNlLnJlc29sdmUoe1xuICAgICAgICAgIGlkOiAxMjM0NSxcbiAgICAgICAgICBuYW1lOiBcInRlc3QtcmVwb1wiLFxuICAgICAgICAgIGZ1bGxfbmFtZTogXCJ0ZXN0LW9yZy90ZXN0LXJlcG9cIixcbiAgICAgICAgICBvd25lcjoge1xuICAgICAgICAgICAgbG9naW46IFwidGVzdC1vcmdcIixcbiAgICAgICAgICAgIGlkOiAyLFxuICAgICAgICAgICAgYXZhdGFyX3VybDogXCJodHRwczovL2F2YXRhcnMuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3UvMlwiLFxuICAgICAgICAgICAgaHRtbF91cmw6IFwiaHR0cHM6Ly9naXRodWIuY29tL3Rlc3Qtb3JnXCIsXG4gICAgICAgICAgICB0eXBlOiBcIk9yZ2FuaXphdGlvblwiLFxuICAgICAgICAgIH0sXG4gICAgICAgICAgaHRtbF91cmw6IFwiaHR0cHM6Ly9naXRodWIuY29tL3Rlc3Qtb3JnL3Rlc3QtcmVwb1wiLFxuICAgICAgICAgIGRlc2NyaXB0aW9uOiBudWxsLFxuICAgICAgICAgIGNyZWF0ZWRfYXQ6IFwiMjAyNC0wMS0wMVQwMDowMDowMFpcIixcbiAgICAgICAgICB1cGRhdGVkX2F0OiBcIjIwMjQtMDEtMDFUMDA6MDA6MDBaXCIsXG4gICAgICAgICAgaG9tZXBhZ2U6IG51bGwsXG4gICAgICAgICAgc2l6ZTogMCxcbiAgICAgICAgICBzdGFyZ2F6ZXJzX2NvdW50OiAwLFxuICAgICAgICAgIHdhdGNoZXJzX2NvdW50OiAwLFxuICAgICAgICAgIGxhbmd1YWdlOiBudWxsLFxuICAgICAgICAgIGxpY2Vuc2U6IG51bGwsXG4gICAgICAgICAgZGVmYXVsdF9icmFuY2g6IFwibWFpblwiLFxuICAgICAgICAgIHRvcGljczogW10sXG4gICAgICAgICAgcHJpdmF0ZTogZmFsc2UsXG4gICAgICAgICAgYXJjaGl2ZWQ6IGZhbHNlLFxuICAgICAgICAgIGdpdF91cmw6IFwiZ2l0Oi8vZ2l0aHViLmNvbS90ZXN0LW9yZy90ZXN0LXJlcG8uZ2l0XCIsXG4gICAgICAgICAgc3NoX3VybDogXCJnaXRAZ2l0aHViLmNvbTp0ZXN0LW9yZy90ZXN0LXJlcG8uZ2l0XCIsXG4gICAgICAgICAgY2xvbmVfdXJsOiBcImh0dHBzOi8vZ2l0aHViLmNvbS90ZXN0LW9yZy90ZXN0LXJlcG8uZ2l0XCIsXG4gICAgICAgIH0pLFxuICAgIH0pXG5cbiAgICBmZXRjaE1vY2subW9ja1Jlc29sdmVkVmFsdWUoe1xuICAgICAgb2s6IHRydWUsXG4gICAgICBqc29uOiAoKSA9PlxuICAgICAgICBQcm9taXNlLnJlc29sdmUoe1xuICAgICAgICAgIGNvbnRlbnQ6IHtcbiAgICAgICAgICAgIG5hbWU6IFwiZmlsZVwiLFxuICAgICAgICAgICAgcGF0aDogXCJmaWxlXCIsXG4gICAgICAgICAgICBzaGE6IFwiYWJjMTIzXCIsXG4gICAgICAgICAgfSxcbiAgICAgICAgfSksXG4gICAgfSlcblxuICAgIGF3YWl0IHNhdmVQYWNrYWdlVG9HaXRodWIobW9ja1BhY2thZ2UsIHtcbiAgICAgIC4uLm1vY2tPcHRpb25zLFxuICAgICAgb3JnOiBcInRlc3Qtb3JnXCIsXG4gICAgfSlcblxuICAgIGNvbnN0IHJlcG9DcmVhdGVDYWxsID0gZmV0Y2hNb2NrLm1vY2suY2FsbHNbMF1cbiAgICBleHBlY3QocmVwb0NyZWF0ZUNhbGwpLnRvQmVEZWZpbmVkKClcbiAgICBpZiAoIXJlcG9DcmVhdGVDYWxsKSByZXR1cm5cblxuICAgIGV4cGVjdChyZXBvQ3JlYXRlQ2FsbFswXSkudG9FcXVhbChcbiAgICAgIFwiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9vcmdzL3Rlc3Qtb3JnL3JlcG9zXCIsXG4gICAgKVxuICB9KVxuXG4gIGl0KFwidXBsb2FkcyByZXNvdXJjZSBmaWxlcyB3aXRoIGJhc2U2NCBlbmNvZGluZ1wiLCBhc3luYyAoKSA9PiB7XG4gICAgZmV0Y2hNb2NrLm1vY2tSZXNvbHZlZFZhbHVlT25jZSh7XG4gICAgICBvazogdHJ1ZSxcbiAgICAgIGpzb246ICgpID0+XG4gICAgICAgIFByb21pc2UucmVzb2x2ZSh7XG4gICAgICAgICAgaWQ6IDEyMzQ1LFxuICAgICAgICAgIG5hbWU6IFwidGVzdC1yZXBvXCIsXG4gICAgICAgICAgZnVsbF9uYW1lOiBcInRlc3QtdXNlci90ZXN0LXJlcG9cIixcbiAgICAgICAgICBvd25lcjoge1xuICAgICAgICAgICAgbG9naW46IFwidGVzdC11c2VyXCIsXG4gICAgICAgICAgICBpZDogMSxcbiAgICAgICAgICAgIGF2YXRhcl91cmw6IFwiaHR0cHM6Ly9hdmF0YXJzLmdpdGh1YnVzZXJjb250ZW50LmNvbS91LzFcIixcbiAgICAgICAgICAgIGh0bWxfdXJsOiBcImh0dHBzOi8vZ2l0aHViLmNvbS90ZXN0LXVzZXJcIixcbiAgICAgICAgICAgIHR5cGU6IFwiVXNlclwiLFxuICAgICAgICAgIH0sXG4gICAgICAgICAgaHRtbF91cmw6IFwiaHR0cHM6Ly9naXRodWIuY29tL3Rlc3QtdXNlci90ZXN0LXJlcG9cIixcbiAgICAgICAgICBkZXNjcmlwdGlvbjogbnVsbCxcbiAgICAgICAgICBjcmVhdGVkX2F0OiBcIjIwMjQtMDEtMDFUMDA6MDA6MDBaXCIsXG4gICAgICAgICAgdXBkYXRlZF9hdDogXCIyMDI0LTAxLTAxVDAwOjAwOjAwWlwiLFxuICAgICAgICAgIGhvbWVwYWdlOiBudWxsLFxuICAgICAgICAgIHNpemU6IDAsXG4gICAgICAgICAgc3RhcmdhemVyc19jb3VudDogMCxcbiAgICAgICAgICB3YXRjaGVyc19jb3VudDogMCxcbiAgICAgICAgICBsYW5ndWFnZTogbnVsbCxcbiAgICAgICAgICBsaWNlbnNlOiBudWxsLFxuICAgICAgICAgIGRlZmF1bHRfYnJhbmNoOiBcIm1haW5cIixcbiAgICAgICAgICB0b3BpY3M6IFtdLFxuICAgICAgICAgIHByaXZhdGU6IGZhbHNlLFxuICAgICAgICAgIGFyY2hpdmVkOiBmYWxzZSxcbiAgICAgICAgICBnaXRfdXJsOiBcImdpdDovL2dpdGh1Yi5jb20vdGVzdC11c2VyL3Rlc3QtcmVwby5naXRcIixcbiAgICAgICAgICBzc2hfdXJsOiBcImdpdEBnaXRodWIuY29tOnRlc3QtdXNlci90ZXN0LXJlcG8uZ2l0XCIsXG4gICAgICAgICAgY2xvbmVfdXJsOiBcImh0dHBzOi8vZ2l0aHViLmNvbS90ZXN0LXVzZXIvdGVzdC1yZXBvLmdpdFwiLFxuICAgICAgICB9KSxcbiAgICB9KVxuXG4gICAgZmV0Y2hNb2NrLm1vY2tSZXNvbHZlZFZhbHVlT25jZSh7XG4gICAgICBvazogdHJ1ZSxcbiAgICAgIGpzb246ICgpID0+XG4gICAgICAgIFByb21pc2UucmVzb2x2ZSh7XG4gICAgICAgICAgY29udGVudDoge1xuICAgICAgICAgICAgbmFtZTogXCJkYXRhLmNzdlwiLFxuICAgICAgICAgICAgcGF0aDogXCJkYXRhLmNzdlwiLFxuICAgICAgICAgICAgc2hhOiBcImFiYzEyM1wiLFxuICAgICAgICAgIH0sXG4gICAgICAgIH0pLFxuICAgIH0pXG5cbiAgICBmZXRjaE1vY2subW9ja1Jlc29sdmVkVmFsdWVPbmNlKHtcbiAgICAgIG9rOiB0cnVlLFxuICAgICAganNvbjogKCkgPT5cbiAgICAgICAgUHJvbWlzZS5yZXNvbHZlKHtcbiAgICAgICAgICBjb250ZW50OiB7XG4gICAgICAgICAgICBuYW1lOiBcImRhdGFwYWNrYWdlLmpzb25cIixcbiAgICAgICAgICAgIHBhdGg6IFwiZGF0YXBhY2thZ2UuanNvblwiLFxuICAgICAgICAgICAgc2hhOiBcImRlZjQ1NlwiLFxuICAgICAgICAgIH0sXG4gICAgICAgIH0pLFxuICAgIH0pXG5cbiAgICBhd2FpdCBzYXZlUGFja2FnZVRvR2l0aHViKG1vY2tQYWNrYWdlLCBtb2NrT3B0aW9ucylcblxuICAgIGNvbnN0IGZpbGVVcGxvYWRDYWxsID0gZmV0Y2hNb2NrLm1vY2suY2FsbHNbMV1cbiAgICBleHBlY3QoZmlsZVVwbG9hZENhbGwpLnRvQmVEZWZpbmVkKClcbiAgICBpZiAoIWZpbGVVcGxvYWRDYWxsKSByZXR1cm5cblxuICAgIGV4cGVjdChmaWxlVXBsb2FkQ2FsbFswXSkudG9FcXVhbChcbiAgICAgIFwiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9yZXBvcy90ZXN0LXVzZXIvdGVzdC1yZXBvL2NvbnRlbnRzL2RhdGEuY3N2XCIsXG4gICAgKVxuICAgIGV4cGVjdChmaWxlVXBsb2FkQ2FsbFsxXSkudG9NYXRjaE9iamVjdCh7XG4gICAgICBtZXRob2Q6IFwiUFVUXCIsXG4gICAgICBoZWFkZXJzOiB7XG4gICAgICAgIEF1dGhvcml6YXRpb246IFwiQmVhcmVyIHRlc3QtYXBpLWtleVwiLFxuICAgICAgICBcIkNvbnRlbnQtVHlwZVwiOiBcImFwcGxpY2F0aW9uL2pzb25cIixcbiAgICAgIH0sXG4gICAgfSlcblxuICAgIGNvbnN0IGZpbGVQYXlsb2FkID0gSlNPTi5wYXJzZShmaWxlVXBsb2FkQ2FsbFsxXS5ib2R5KVxuICAgIGV4cGVjdChmaWxlUGF5bG9hZC5wYXRoKS50b0VxdWFsKFwiZGF0YS5jc3ZcIilcbiAgICBleHBlY3QoZmlsZVBheWxvYWQubWVzc2FnZSkudG9FcXVhbCgnQWRkZWQgZmlsZSBcImRhdGEuY3N2XCInKVxuICAgIGV4cGVjdChmaWxlUGF5bG9hZC5jb250ZW50KS50b0JlRGVmaW5lZCgpXG4gICAgZXhwZWN0KHR5cGVvZiBmaWxlUGF5bG9hZC5jb250ZW50KS50b0VxdWFsKFwic3RyaW5nXCIpXG4gIH0pXG5cbiAgaXQoXCJ1cGxvYWRzIGRhdGFwYWNrYWdlLmpzb24gbWV0YWRhdGEgZmlsZVwiLCBhc3luYyAoKSA9PiB7XG4gICAgZmV0Y2hNb2NrLm1vY2tSZXNvbHZlZFZhbHVlT25jZSh7XG4gICAgICBvazogdHJ1ZSxcbiAgICAgIGpzb246ICgpID0+XG4gICAgICAgIFByb21pc2UucmVzb2x2ZSh7XG4gICAgICAgICAgaWQ6IDEyMzQ1LFxuICAgICAgICAgIG5hbWU6IFwidGVzdC1yZXBvXCIsXG4gICAgICAgICAgZnVsbF9uYW1lOiBcInRlc3QtdXNlci90ZXN0LXJlcG9cIixcbiAgICAgICAgICBvd25lcjoge1xuICAgICAgICAgICAgbG9naW46IFwidGVzdC11c2VyXCIsXG4gICAgICAgICAgICBpZDogMSxcbiAgICAgICAgICAgIGF2YXRhcl91cmw6IFwiaHR0cHM6Ly9hdmF0YXJzLmdpdGh1YnVzZXJjb250ZW50LmNvbS91LzFcIixcbiAgICAgICAgICAgIGh0bWxfdXJsOiBcImh0dHBzOi8vZ2l0aHViLmNvbS90ZXN0LXVzZXJcIixcbiAgICAgICAgICAgIHR5cGU6IFwiVXNlclwiLFxuICAgICAgICAgIH0sXG4gICAgICAgICAgaHRtbF91cmw6IFwiaHR0cHM6Ly9naXRodWIuY29tL3Rlc3QtdXNlci90ZXN0LXJlcG9cIixcbiAgICAgICAgICBkZXNjcmlwdGlvbjogbnVsbCxcbiAgICAgICAgICBjcmVhdGVkX2F0OiBcIjIwMjQtMDEtMDFUMDA6MDA6MDBaXCIsXG4gICAgICAgICAgdXBkYXRlZF9hdDogXCIyMDI0LTAxLTAxVDAwOjAwOjAwWlwiLFxuICAgICAgICAgIGhvbWVwYWdlOiBudWxsLFxuICAgICAgICAgIHNpemU6IDAsXG4gICAgICAgICAgc3RhcmdhemVyc19jb3VudDogMCxcbiAgICAgICAgICB3YXRjaGVyc19jb3VudDogMCxcbiAgICAgICAgICBsYW5ndWFnZTogbnVsbCxcbiAgICAgICAgICBsaWNlbnNlOiBudWxsLFxuICAgICAgICAgIGRlZmF1bHRfYnJhbmNoOiBcIm1haW5cIixcbiAgICAgICAgICB0b3BpY3M6IFtdLFxuICAgICAgICAgIHByaXZhdGU6IGZhbHNlLFxuICAgICAgICAgIGFyY2hpdmVkOiBmYWxzZSxcbiAgICAgICAgICBnaXRfdXJsOiBcImdpdDovL2dpdGh1Yi5jb20vdGVzdC11c2VyL3Rlc3QtcmVwby5naXRcIixcbiAgICAgICAgICBzc2hfdXJsOiBcImdpdEBnaXRodWIuY29tOnRlc3QtdXNlci90ZXN0LXJlcG8uZ2l0XCIsXG4gICAgICAgICAgY2xvbmVfdXJsOiBcImh0dHBzOi8vZ2l0aHViLmNvbS90ZXN0LXVzZXIvdGVzdC1yZXBvLmdpdFwiLFxuICAgICAgICB9KSxcbiAgICB9KVxuXG4gICAgZmV0Y2hNb2NrLm1vY2tSZXNvbHZlZFZhbHVlT25jZSh7XG4gICAgICBvazogdHJ1ZSxcbiAgICAgIGpzb246ICgpID0+XG4gICAgICAgIFByb21pc2UucmVzb2x2ZSh7XG4gICAgICAgICAgY29udGVudDoge1xuICAgICAgICAgICAgbmFtZTogXCJkYXRhLmNzdlwiLFxuICAgICAgICAgICAgcGF0aDogXCJkYXRhLmNzdlwiLFxuICAgICAgICAgICAgc2hhOiBcImFiYzEyM1wiLFxuICAgICAgICAgIH0sXG4gICAgICAgIH0pLFxuICAgIH0pXG5cbiAgICBmZXRjaE1vY2subW9ja1Jlc29sdmVkVmFsdWVPbmNlKHtcbiAgICAgIG9rOiB0cnVlLFxuICAgICAganNvbjogKCkgPT5cbiAgICAgICAgUHJvbWlzZS5yZXNvbHZlKHtcbiAgICAgICAgICBjb250ZW50OiB7XG4gICAgICAgICAgICBuYW1lOiBcImRhdGFwYWNrYWdlLmpzb25cIixcbiAgICAgICAgICAgIHBhdGg6IFwiZGF0YXBhY2thZ2UuanNvblwiLFxuICAgICAgICAgICAgc2hhOiBcImRlZjQ1NlwiLFxuICAgICAgICAgIH0sXG4gICAgICAgIH0pLFxuICAgIH0pXG5cbiAgICBhd2FpdCBzYXZlUGFja2FnZVRvR2l0aHViKG1vY2tQYWNrYWdlLCBtb2NrT3B0aW9ucylcblxuICAgIGNvbnN0IGRhdGFwYWNrYWdlVXBsb2FkQ2FsbCA9IGZldGNoTW9jay5tb2NrLmNhbGxzWzJdXG4gICAgZXhwZWN0KGRhdGFwYWNrYWdlVXBsb2FkQ2FsbCkudG9CZURlZmluZWQoKVxuICAgIGlmICghZGF0YXBhY2thZ2VVcGxvYWRDYWxsKSByZXR1cm5cblxuICAgIGV4cGVjdChkYXRhcGFja2FnZVVwbG9hZENhbGxbMF0pLnRvRXF1YWwoXG4gICAgICBcImh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vcmVwb3MvdGVzdC11c2VyL3Rlc3QtcmVwby9jb250ZW50cy9kYXRhcGFja2FnZS5qc29uXCIsXG4gICAgKVxuXG4gICAgY29uc3QgZGF0YXBhY2thZ2VQYXlsb2FkID0gSlNPTi5wYXJzZShkYXRhcGFja2FnZVVwbG9hZENhbGxbMV0uYm9keSlcbiAgICBleHBlY3QoZGF0YXBhY2thZ2VQYXlsb2FkLnBhdGgpLnRvRXF1YWwoXCJkYXRhcGFja2FnZS5qc29uXCIpXG4gICAgZXhwZWN0KGRhdGFwYWNrYWdlUGF5bG9hZC5tZXNzYWdlKS50b0VxdWFsKCdBZGRlZCBmaWxlIFwiZGF0YXBhY2thZ2UuanNvblwiJylcbiAgICBleHBlY3QoZGF0YXBhY2thZ2VQYXlsb2FkLmNvbnRlbnQpLnRvQmVEZWZpbmVkKClcbiAgICBleHBlY3QodHlwZW9mIGRhdGFwYWNrYWdlUGF5bG9hZC5jb250ZW50KS50b0VxdWFsKFwic3RyaW5nXCIpXG4gIH0pXG5cbiAgaXQoXCJwYXNzZXMgQVBJIGtleSBhcyBCZWFyZXIgdG9rZW4gaW4gQXV0aG9yaXphdGlvbiBoZWFkZXJcIiwgYXN5bmMgKCkgPT4ge1xuICAgIGZldGNoTW9jay5tb2NrUmVzb2x2ZWRWYWx1ZSh7XG4gICAgICBvazogdHJ1ZSxcbiAgICAgIGpzb246ICgpID0+XG4gICAgICAgIFByb21pc2UucmVzb2x2ZSh7XG4gICAgICAgICAgaWQ6IDEyMzQ1LFxuICAgICAgICAgIG5hbWU6IFwidGVzdC1yZXBvXCIsXG4gICAgICAgICAgb3duZXI6IHtcbiAgICAgICAgICAgIGxvZ2luOiBcInRlc3QtdXNlclwiLFxuICAgICAgICAgICAgaWQ6IDEsXG4gICAgICAgICAgICBhdmF0YXJfdXJsOiBcImh0dHBzOi8vYXZhdGFycy5naXRodWJ1c2VyY29udGVudC5jb20vdS8xXCIsXG4gICAgICAgICAgICBodG1sX3VybDogXCJodHRwczovL2dpdGh1Yi5jb20vdGVzdC11c2VyXCIsXG4gICAgICAgICAgICB0eXBlOiBcIlVzZXJcIixcbiAgICAgICAgICB9LFxuICAgICAgICAgIGh0bWxfdXJsOiBcImh0dHBzOi8vZ2l0aHViLmNvbS90ZXN0LXVzZXIvdGVzdC1yZXBvXCIsXG4gICAgICAgICAgZGVzY3JpcHRpb246IG51bGwsXG4gICAgICAgICAgY3JlYXRlZF9hdDogXCIyMDI0LTAxLTAxVDAwOjAwOjAwWlwiLFxuICAgICAgICAgIHVwZGF0ZWRfYXQ6IFwiMjAyNC0wMS0wMVQwMDowMDowMFpcIixcbiAgICAgICAgICBob21lcGFnZTogbnVsbCxcbiAgICAgICAgICBzaXplOiAwLFxuICAgICAgICAgIHN0YXJnYXplcnNfY291bnQ6IDAsXG4gICAgICAgICAgd2F0Y2hlcnNfY291bnQ6IDAsXG4gICAgICAgICAgbGFuZ3VhZ2U6IG51bGwsXG4gICAgICAgICAgbGljZW5zZTogbnVsbCxcbiAgICAgICAgICBkZWZhdWx0X2JyYW5jaDogXCJtYWluXCIsXG4gICAgICAgICAgdG9waWNzOiBbXSxcbiAgICAgICAgICBwcml2YXRlOiBmYWxzZSxcbiAgICAgICAgICBhcmNoaXZlZDogZmFsc2UsXG4gICAgICAgICAgZ2l0X3VybDogXCJnaXQ6Ly9naXRodWIuY29tL3Rlc3QtdXNlci90ZXN0LXJlcG8uZ2l0XCIsXG4gICAgICAgICAgc3NoX3VybDogXCJnaXRAZ2l0aHViLmNvbTp0ZXN0LXVzZXIvdGVzdC1yZXBvLmdpdFwiLFxuICAgICAgICAgIGNsb25lX3VybDogXCJodHRwczovL2dpdGh1Yi5jb20vdGVzdC11c2VyL3Rlc3QtcmVwby5naXRcIixcbiAgICAgICAgfSksXG4gICAgfSlcblxuICAgIGF3YWl0IHNhdmVQYWNrYWdlVG9HaXRodWIobW9ja1BhY2thZ2UsIHtcbiAgICAgIC4uLm1vY2tPcHRpb25zLFxuICAgICAgYXBpS2V5OiBcImN1c3RvbS1hcGkta2V5XCIsXG4gICAgfSlcblxuICAgIGNvbnN0IGZpcnN0Q2FsbCA9IGZldGNoTW9jay5tb2NrLmNhbGxzWzBdXG4gICAgZXhwZWN0KGZpcnN0Q2FsbCkudG9CZURlZmluZWQoKVxuICAgIGlmICghZmlyc3RDYWxsKSByZXR1cm5cblxuICAgIGNvbnN0IGhlYWRlcnMgPSBmaXJzdENhbGxbMV0uaGVhZGVyc1xuICAgIGV4cGVjdChoZWFkZXJzLkF1dGhvcml6YXRpb24pLnRvRXF1YWwoXCJCZWFyZXIgY3VzdG9tLWFwaS1rZXlcIilcbiAgfSlcblxuICBpdChcImhhbmRsZXMgQVBJIGVycm9ycyBmcm9tIHJlcG9zaXRvcnkgY3JlYXRpb25cIiwgYXN5bmMgKCkgPT4ge1xuICAgIGZldGNoTW9jay5tb2NrUmVzb2x2ZWRWYWx1ZU9uY2Uoe1xuICAgICAgb2s6IGZhbHNlLFxuICAgICAgc3RhdHVzOiA0MDAsXG4gICAgICBzdGF0dXNUZXh0OiBcIkJhZCBSZXF1ZXN0XCIsXG4gICAgICB0ZXh0OiAoKSA9PiBQcm9taXNlLnJlc29sdmUoXCJSZXBvc2l0b3J5IG5hbWUgYWxyZWFkeSBleGlzdHNcIiksXG4gICAgfSlcblxuICAgIGF3YWl0IGV4cGVjdChzYXZlUGFja2FnZVRvR2l0aHViKG1vY2tQYWNrYWdlLCBtb2NrT3B0aW9ucykpLnJlamVjdHMudG9UaHJvdyhcbiAgICAgIFwiR2l0aHViIEFQSSBlcnJvcjogNDAwIEJhZCBSZXF1ZXN0XCIsXG4gICAgKVxuICB9KVxuXG4gIGl0KFwiaGFuZGxlcyBBUEkgZXJyb3JzIGZyb20gZmlsZSB1cGxvYWRcIiwgYXN5bmMgKCkgPT4ge1xuICAgIGZldGNoTW9jay5tb2NrUmVzb2x2ZWRWYWx1ZU9uY2Uoe1xuICAgICAgb2s6IHRydWUsXG4gICAgICBqc29uOiAoKSA9PlxuICAgICAgICBQcm9taXNlLnJlc29sdmUoe1xuICAgICAgICAgIGlkOiAxMjM0NSxcbiAgICAgICAgICBuYW1lOiBcInRlc3QtcmVwb1wiLFxuICAgICAgICAgIGZ1bGxfbmFtZTogXCJ0ZXN0LXVzZXIvdGVzdC1yZXBvXCIsXG4gICAgICAgICAgb3duZXI6IHtcbiAgICAgICAgICAgIGxvZ2luOiBcInRlc3QtdXNlclwiLFxuICAgICAgICAgICAgaWQ6IDEsXG4gICAgICAgICAgICBhdmF0YXJfdXJsOiBcImh0dHBzOi8vYXZhdGFycy5naXRodWJ1c2VyY29udGVudC5jb20vdS8xXCIsXG4gICAgICAgICAgICBodG1sX3VybDogXCJodHRwczovL2dpdGh1Yi5jb20vdGVzdC11c2VyXCIsXG4gICAgICAgICAgICB0eXBlOiBcIlVzZXJcIixcbiAgICAgICAgICB9LFxuICAgICAgICAgIGh0bWxfdXJsOiBcImh0dHBzOi8vZ2l0aHViLmNvbS90ZXN0LXVzZXIvdGVzdC1yZXBvXCIsXG4gICAgICAgICAgZGVzY3JpcHRpb246IG51bGwsXG4gICAgICAgICAgY3JlYXRlZF9hdDogXCIyMDI0LTAxLTAxVDAwOjAwOjAwWlwiLFxuICAgICAgICAgIHVwZGF0ZWRfYXQ6IFwiMjAyNC0wMS0wMVQwMDowMDowMFpcIixcbiAgICAgICAgICBob21lcGFnZTogbnVsbCxcbiAgICAgICAgICBzaXplOiAwLFxuICAgICAgICAgIHN0YXJnYXplcnNfY291bnQ6IDAsXG4gICAgICAgICAgd2F0Y2hlcnNfY291bnQ6IDAsXG4gICAgICAgICAgbGFuZ3VhZ2U6IG51bGwsXG4gICAgICAgICAgbGljZW5zZTogbnVsbCxcbiAgICAgICAgICBkZWZhdWx0X2JyYW5jaDogXCJtYWluXCIsXG4gICAgICAgICAgdG9waWNzOiBbXSxcbiAgICAgICAgICBwcml2YXRlOiBmYWxzZSxcbiAgICAgICAgICBhcmNoaXZlZDogZmFsc2UsXG4gICAgICAgICAgZ2l0X3VybDogXCJnaXQ6Ly9naXRodWIuY29tL3Rlc3QtdXNlci90ZXN0LXJlcG8uZ2l0XCIsXG4gICAgICAgICAgc3NoX3VybDogXCJnaXRAZ2l0aHViLmNvbTp0ZXN0LXVzZXIvdGVzdC1yZXBvLmdpdFwiLFxuICAgICAgICAgIGNsb25lX3VybDogXCJodHRwczovL2dpdGh1Yi5jb20vdGVzdC11c2VyL3Rlc3QtcmVwby5naXRcIixcbiAgICAgICAgfSksXG4gICAgfSlcblxuICAgIGZldGNoTW9jay5tb2NrUmVzb2x2ZWRWYWx1ZU9uY2Uoe1xuICAgICAgb2s6IGZhbHNlLFxuICAgICAgc3RhdHVzOiA1MDAsXG4gICAgICBzdGF0dXNUZXh0OiBcIkludGVybmFsIFNlcnZlciBFcnJvclwiLFxuICAgICAgdGV4dDogKCkgPT4gUHJvbWlzZS5yZXNvbHZlKFwiRmFpbGVkIHRvIHVwbG9hZCBmaWxlXCIpLFxuICAgIH0pXG5cbiAgICBhd2FpdCBleHBlY3Qoc2F2ZVBhY2thZ2VUb0dpdGh1Yihtb2NrUGFja2FnZSwgbW9ja09wdGlvbnMpKS5yZWplY3RzLnRvVGhyb3coXG4gICAgICBcIkdpdGh1YiBBUEkgZXJyb3I6IDUwMCBJbnRlcm5hbCBTZXJ2ZXIgRXJyb3JcIixcbiAgICApXG4gIH0pXG5cbiAgaXQoXCJoYW5kbGVzIHBhY2thZ2VzIHdpdGggbXVsdGlwbGUgcmVzb3VyY2VzXCIsIGFzeW5jICgpID0+IHtcbiAgICBjb25zdCBtdWx0aVJlc291cmNlUGFja2FnZTogUGFja2FnZSA9IHtcbiAgICAgIC4uLm1vY2tQYWNrYWdlLFxuICAgICAgcmVzb3VyY2VzOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBuYW1lOiBcInJlc291cmNlLTFcIixcbiAgICAgICAgICBwYXRoOiBnZXRGaXh0dXJlUGF0aChcImRhdGEuY3N2XCIpLFxuICAgICAgICAgIGZvcm1hdDogXCJjc3ZcIixcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIG5hbWU6IFwicmVzb3VyY2UtMlwiLFxuICAgICAgICAgIHBhdGg6IGdldEZpeHR1cmVQYXRoKFwiZGF0YS5jc3ZcIiksXG4gICAgICAgICAgZm9ybWF0OiBcImpzb25cIixcbiAgICAgICAgfSxcbiAgICAgIF0sXG4gICAgfVxuXG4gICAgZmV0Y2hNb2NrLm1vY2tSZXNvbHZlZFZhbHVlKHtcbiAgICAgIG9rOiB0cnVlLFxuICAgICAganNvbjogKCkgPT5cbiAgICAgICAgUHJvbWlzZS5yZXNvbHZlKHtcbiAgICAgICAgICBpZDogMTIzNDUsXG4gICAgICAgICAgbmFtZTogXCJ0ZXN0LXJlcG9cIixcbiAgICAgICAgICBmdWxsX25hbWU6IFwidGVzdC11c2VyL3Rlc3QtcmVwb1wiLFxuICAgICAgICAgIG93bmVyOiB7XG4gICAgICAgICAgICBsb2dpbjogXCJ0ZXN0LXVzZXJcIixcbiAgICAgICAgICAgIGlkOiAxLFxuICAgICAgICAgICAgYXZhdGFyX3VybDogXCJodHRwczovL2F2YXRhcnMuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3UvMVwiLFxuICAgICAgICAgICAgaHRtbF91cmw6IFwiaHR0cHM6Ly9naXRodWIuY29tL3Rlc3QtdXNlclwiLFxuICAgICAgICAgICAgdHlwZTogXCJVc2VyXCIsXG4gICAgICAgICAgfSxcbiAgICAgICAgICBodG1sX3VybDogXCJodHRwczovL2dpdGh1Yi5jb20vdGVzdC11c2VyL3Rlc3QtcmVwb1wiLFxuICAgICAgICAgIGRlc2NyaXB0aW9uOiBudWxsLFxuICAgICAgICAgIGNyZWF0ZWRfYXQ6IFwiMjAyNC0wMS0wMVQwMDowMDowMFpcIixcbiAgICAgICAgICB1cGRhdGVkX2F0OiBcIjIwMjQtMDEtMDFUMDA6MDA6MDBaXCIsXG4gICAgICAgICAgaG9tZXBhZ2U6IG51bGwsXG4gICAgICAgICAgc2l6ZTogMCxcbiAgICAgICAgICBzdGFyZ2F6ZXJzX2NvdW50OiAwLFxuICAgICAgICAgIHdhdGNoZXJzX2NvdW50OiAwLFxuICAgICAgICAgIGxhbmd1YWdlOiBudWxsLFxuICAgICAgICAgIGxpY2Vuc2U6IG51bGwsXG4gICAgICAgICAgZGVmYXVsdF9icmFuY2g6IFwibWFpblwiLFxuICAgICAgICAgIHRvcGljczogW10sXG4gICAgICAgICAgcHJpdmF0ZTogZmFsc2UsXG4gICAgICAgICAgYXJjaGl2ZWQ6IGZhbHNlLFxuICAgICAgICAgIGdpdF91cmw6IFwiZ2l0Oi8vZ2l0aHViLmNvbS90ZXN0LXVzZXIvdGVzdC1yZXBvLmdpdFwiLFxuICAgICAgICAgIHNzaF91cmw6IFwiZ2l0QGdpdGh1Yi5jb206dGVzdC11c2VyL3Rlc3QtcmVwby5naXRcIixcbiAgICAgICAgICBjbG9uZV91cmw6IFwiaHR0cHM6Ly9naXRodWIuY29tL3Rlc3QtdXNlci90ZXN0LXJlcG8uZ2l0XCIsXG4gICAgICAgIH0pLFxuICAgIH0pXG5cbiAgICBhd2FpdCBzYXZlUGFja2FnZVRvR2l0aHViKG11bHRpUmVzb3VyY2VQYWNrYWdlLCBtb2NrT3B0aW9ucylcblxuICAgIGV4cGVjdChmZXRjaE1vY2spLnRvSGF2ZUJlZW5DYWxsZWRUaW1lcyg0KVxuXG4gICAgY29uc3Qgc2Vjb25kRmlsZVVwbG9hZENhbGwgPSBmZXRjaE1vY2subW9jay5jYWxsc1syXVxuICAgIGV4cGVjdChzZWNvbmRGaWxlVXBsb2FkQ2FsbCkudG9CZURlZmluZWQoKVxuICAgIGlmICghc2Vjb25kRmlsZVVwbG9hZENhbGwpIHJldHVyblxuXG4gICAgZXhwZWN0KHNlY29uZEZpbGVVcGxvYWRDYWxsWzBdKS50b0NvbnRhaW4oXCIvY29udGVudHMvXCIpXG4gIH0pXG5cbiAgaXQoXCJoYW5kbGVzIHBhY2thZ2VzIHdpdGggbm8gcmVzb3VyY2VzXCIsIGFzeW5jICgpID0+IHtcbiAgICBjb25zdCBlbXB0eVBhY2thZ2U6IFBhY2thZ2UgPSB7XG4gICAgICAuLi5tb2NrUGFja2FnZSxcbiAgICAgIHJlc291cmNlczogW10sXG4gICAgfVxuXG4gICAgZmV0Y2hNb2NrLm1vY2tSZXNvbHZlZFZhbHVlT25jZSh7XG4gICAgICBvazogdHJ1ZSxcbiAgICAgIGpzb246ICgpID0+XG4gICAgICAgIFByb21pc2UucmVzb2x2ZSh7XG4gICAgICAgICAgaWQ6IDEyMzQ1LFxuICAgICAgICAgIG5hbWU6IFwidGVzdC1yZXBvXCIsXG4gICAgICAgICAgZnVsbF9uYW1lOiBcInRlc3QtdXNlci90ZXN0LXJlcG9cIixcbiAgICAgICAgICBvd25lcjoge1xuICAgICAgICAgICAgbG9naW46IFwidGVzdC11c2VyXCIsXG4gICAgICAgICAgICBpZDogMSxcbiAgICAgICAgICAgIGF2YXRhcl91cmw6IFwiaHR0cHM6Ly9hdmF0YXJzLmdpdGh1YnVzZXJjb250ZW50LmNvbS91LzFcIixcbiAgICAgICAgICAgIGh0bWxfdXJsOiBcImh0dHBzOi8vZ2l0aHViLmNvbS90ZXN0LXVzZXJcIixcbiAgICAgICAgICAgIHR5cGU6IFwiVXNlclwiLFxuICAgICAgICAgIH0sXG4gICAgICAgICAgaHRtbF91cmw6IFwiaHR0cHM6Ly9naXRodWIuY29tL3Rlc3QtdXNlci90ZXN0LXJlcG9cIixcbiAgICAgICAgICBkZXNjcmlwdGlvbjogbnVsbCxcbiAgICAgICAgICBjcmVhdGVkX2F0OiBcIjIwMjQtMDEtMDFUMDA6MDA6MDBaXCIsXG4gICAgICAgICAgdXBkYXRlZF9hdDogXCIyMDI0LTAxLTAxVDAwOjAwOjAwWlwiLFxuICAgICAgICAgIGhvbWVwYWdlOiBudWxsLFxuICAgICAgICAgIHNpemU6IDAsXG4gICAgICAgICAgc3RhcmdhemVyc19jb3VudDogMCxcbiAgICAgICAgICB3YXRjaGVyc19jb3VudDogMCxcbiAgICAgICAgICBsYW5ndWFnZTogbnVsbCxcbiAgICAgICAgICBsaWNlbnNlOiBudWxsLFxuICAgICAgICAgIGRlZmF1bHRfYnJhbmNoOiBcIm1haW5cIixcbiAgICAgICAgICB0b3BpY3M6IFtdLFxuICAgICAgICAgIHByaXZhdGU6IGZhbHNlLFxuICAgICAgICAgIGFyY2hpdmVkOiBmYWxzZSxcbiAgICAgICAgICBnaXRfdXJsOiBcImdpdDovL2dpdGh1Yi5jb20vdGVzdC11c2VyL3Rlc3QtcmVwby5naXRcIixcbiAgICAgICAgICBzc2hfdXJsOiBcImdpdEBnaXRodWIuY29tOnRlc3QtdXNlci90ZXN0LXJlcG8uZ2l0XCIsXG4gICAgICAgICAgY2xvbmVfdXJsOiBcImh0dHBzOi8vZ2l0aHViLmNvbS90ZXN0LXVzZXIvdGVzdC1yZXBvLmdpdFwiLFxuICAgICAgICB9KSxcbiAgICB9KVxuXG4gICAgZmV0Y2hNb2NrLm1vY2tSZXNvbHZlZFZhbHVlT25jZSh7XG4gICAgICBvazogdHJ1ZSxcbiAgICAgIGpzb246ICgpID0+XG4gICAgICAgIFByb21pc2UucmVzb2x2ZSh7XG4gICAgICAgICAgY29udGVudDoge1xuICAgICAgICAgICAgbmFtZTogXCJkYXRhcGFja2FnZS5qc29uXCIsXG4gICAgICAgICAgICBwYXRoOiBcImRhdGFwYWNrYWdlLmpzb25cIixcbiAgICAgICAgICAgIHNoYTogXCJkZWY0NTZcIixcbiAgICAgICAgICB9LFxuICAgICAgICB9KSxcbiAgICB9KVxuXG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgc2F2ZVBhY2thZ2VUb0dpdGh1YihlbXB0eVBhY2thZ2UsIG1vY2tPcHRpb25zKVxuXG4gICAgZXhwZWN0KGZldGNoTW9jaykudG9IYXZlQmVlbkNhbGxlZFRpbWVzKDIpXG4gICAgZXhwZWN0KHJlc3VsdC5yZXBvVXJsKS50b0VxdWFsKFwiaHR0cHM6Ly9naXRodWIuY29tL3Rlc3QtdXNlci90ZXN0LXJlcG9cIilcbiAgfSlcblxuICBpdChcInNraXBzIHJlc291cmNlcyB3aXRob3V0IHBhdGhcIiwgYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IHBhY2thZ2VXaXRob3V0UGF0aDogUGFja2FnZSA9IHtcbiAgICAgIC4uLm1vY2tQYWNrYWdlLFxuICAgICAgcmVzb3VyY2VzOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBuYW1lOiBcInJlc291cmNlLXdpdGhvdXQtcGF0aFwiLFxuICAgICAgICAgIGZvcm1hdDogXCJjc3ZcIixcbiAgICAgICAgfSxcbiAgICAgIF0sXG4gICAgfVxuXG4gICAgZmV0Y2hNb2NrLm1vY2tSZXNvbHZlZFZhbHVlT25jZSh7XG4gICAgICBvazogdHJ1ZSxcbiAgICAgIGpzb246ICgpID0+XG4gICAgICAgIFByb21pc2UucmVzb2x2ZSh7XG4gICAgICAgICAgaWQ6IDEyMzQ1LFxuICAgICAgICAgIG5hbWU6IFwidGVzdC1yZXBvXCIsXG4gICAgICAgICAgZnVsbF9uYW1lOiBcInRlc3QtdXNlci90ZXN0LXJlcG9cIixcbiAgICAgICAgICBvd25lcjoge1xuICAgICAgICAgICAgbG9naW46IFwidGVzdC11c2VyXCIsXG4gICAgICAgICAgICBpZDogMSxcbiAgICAgICAgICAgIGF2YXRhcl91cmw6IFwiaHR0cHM6Ly9hdmF0YXJzLmdpdGh1YnVzZXJjb250ZW50LmNvbS91LzFcIixcbiAgICAgICAgICAgIGh0bWxfdXJsOiBcImh0dHBzOi8vZ2l0aHViLmNvbS90ZXN0LXVzZXJcIixcbiAgICAgICAgICAgIHR5cGU6IFwiVXNlclwiLFxuICAgICAgICAgIH0sXG4gICAgICAgICAgaHRtbF91cmw6IFwiaHR0cHM6Ly9naXRodWIuY29tL3Rlc3QtdXNlci90ZXN0LXJlcG9cIixcbiAgICAgICAgICBkZXNjcmlwdGlvbjogbnVsbCxcbiAgICAgICAgICBjcmVhdGVkX2F0OiBcIjIwMjQtMDEtMDFUMDA6MDA6MDBaXCIsXG4gICAgICAgICAgdXBkYXRlZF9hdDogXCIyMDI0LTAxLTAxVDAwOjAwOjAwWlwiLFxuICAgICAgICAgIGhvbWVwYWdlOiBudWxsLFxuICAgICAgICAgIHNpemU6IDAsXG4gICAgICAgICAgc3RhcmdhemVyc19jb3VudDogMCxcbiAgICAgICAgICB3YXRjaGVyc19jb3VudDogMCxcbiAgICAgICAgICBsYW5ndWFnZTogbnVsbCxcbiAgICAgICAgICBsaWNlbnNlOiBudWxsLFxuICAgICAgICAgIGRlZmF1bHRfYnJhbmNoOiBcIm1haW5cIixcbiAgICAgICAgICB0b3BpY3M6IFtdLFxuICAgICAgICAgIHByaXZhdGU6IGZhbHNlLFxuICAgICAgICAgIGFyY2hpdmVkOiBmYWxzZSxcbiAgICAgICAgICBnaXRfdXJsOiBcImdpdDovL2dpdGh1Yi5jb20vdGVzdC11c2VyL3Rlc3QtcmVwby5naXRcIixcbiAgICAgICAgICBzc2hfdXJsOiBcImdpdEBnaXRodWIuY29tOnRlc3QtdXNlci90ZXN0LXJlcG8uZ2l0XCIsXG4gICAgICAgICAgY2xvbmVfdXJsOiBcImh0dHBzOi8vZ2l0aHViLmNvbS90ZXN0LXVzZXIvdGVzdC1yZXBvLmdpdFwiLFxuICAgICAgICB9KSxcbiAgICB9KVxuXG4gICAgZmV0Y2hNb2NrLm1vY2tSZXNvbHZlZFZhbHVlT25jZSh7XG4gICAgICBvazogdHJ1ZSxcbiAgICAgIGpzb246ICgpID0+XG4gICAgICAgIFByb21pc2UucmVzb2x2ZSh7XG4gICAgICAgICAgY29udGVudDoge1xuICAgICAgICAgICAgbmFtZTogXCJkYXRhcGFja2FnZS5qc29uXCIsXG4gICAgICAgICAgICBwYXRoOiBcImRhdGFwYWNrYWdlLmpzb25cIixcbiAgICAgICAgICAgIHNoYTogXCJkZWY0NTZcIixcbiAgICAgICAgICB9LFxuICAgICAgICB9KSxcbiAgICB9KVxuXG4gICAgYXdhaXQgc2F2ZVBhY2thZ2VUb0dpdGh1YihwYWNrYWdlV2l0aG91dFBhdGgsIG1vY2tPcHRpb25zKVxuXG4gICAgZXhwZWN0KGZldGNoTW9jaykudG9IYXZlQmVlbkNhbGxlZFRpbWVzKDIpXG4gIH0pXG59KVxuIl19