]> source.dussan.org Git - sonarqube.git/blob
ad79409fd34d89a7975d520d96d98248efa4450f
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2020 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 3 of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20 package org.sonar.server.platform.db.migration.version.v86;
21
22 import java.sql.SQLException;
23 import java.util.Map;
24 import javax.annotation.Nullable;
25 import org.junit.Rule;
26 import org.junit.Test;
27 import org.sonar.api.impl.utils.TestSystem2;
28 import org.sonar.api.utils.System2;
29 import org.sonar.core.util.UuidFactory;
30 import org.sonar.core.util.UuidFactoryFast;
31 import org.sonar.db.CoreDbTester;
32 import org.sonar.server.platform.db.migration.step.MigrationStep;
33 import org.xmlunit.builder.DiffBuilder;
34 import org.xmlunit.builder.Input;
35 import org.xmlunit.diff.Diff;
36
37 import static org.assertj.core.api.Assertions.assertThat;
38 import static org.assertj.core.api.Assertions.assertThatThrownBy;
39 import static org.assertj.core.api.Assertions.tuple;
40 import static org.sonar.server.platform.db.migration.version.v86.MigrateApplicationDefinitionsFromXmlToDb.TEXT_VALUE_MAX_LENGTH;
41
42 public class MigrateApplicationDefinitionsFromXmlToDbTest {
43   private static final String QUALIFIER_PROJECT = "TRK";
44   private static final String QUALIFIER_APP = "APP";
45   private static final long NOW = 100_000_000_000L;
46
47   private static final String PROJECT_1_UUID = "proj1-uuid";
48   private static final String PROJECT_1_MASTER_BRANCH_UUID = "proj1-master-uuid";
49   private static final String PROJECT_1_BRANCH_1_UUID = "proj1-branch1-uuid";
50   private static final String PROJECT_1_BRANCH_2_UUID = "proj1-branch2-uuid";
51   private static final String PROJECT_2_UUID = "proj2-uuid";
52   private static final String PROJECT_2_MASTER_BRANCH_UUID = "proj2-master-uuid";
53   private static final String PROJECT_2_BRANCH_1_UUID = "proj2-branch1-uuid";
54   private static final String APP_1_UUID = "app1-uuid";
55   private static final String APP_1_MASTER_BRANCH_UUID = "app1-master-uuid";
56   private static final String APP_1_BRANCH_1_UUID = "app1-branch1-uuid";
57   private static final String APP_1_BRANCH_2_UUID = "app1-branch2-uuid";
58   private static final String APP_2_UUID = "app2-uuid";
59   private static final String APP_2_MASTER_BRANCH_UUID = "app2-master-uuid";
60   private static final String APP_2_BRANCH_1_UUID = "app2-branch1-uuid";
61   private static final String EMPTY_XML = "<views></views>";
62
63   private static final String EMPTY_APP_XML = "<views>\n" +
64     "    <vw key=\"app1\" def=\"false\">\n" +
65     "        <name><![CDATA[app1]]></name>\n" +
66     "        <desc><![CDATA[]]></desc>\n" +
67     "        <qualifier><![CDATA[APP]]></qualifier>\n" +
68     "    </vw>\n" +
69     "</views>";
70
71   private static final String APP_WITH_NO_BRANCHES_XML = "<views>\n" +
72     "    <vw key=\"app1-key\" def=\"false\">\n" +
73     "        <name><![CDATA[app1-key]]></name>\n" +
74     "        <desc><![CDATA[]]></desc>\n" +
75     "        <qualifier><![CDATA[APP]]></qualifier>\n" +
76     "        <p>proj1-key</p>\n" +
77     "        <p>proj2-key</p>\n" +
78     "    </vw>\n" +
79     "</views>";
80
81   private static final String COMPLEX_XML_BEFORE = "<views>\n" +
82     "    <vw key=\"app1-key\" def=\"false\">\n" +
83     "        <name><![CDATA[app1-key]]></name>\n" +
84     "        <desc><![CDATA[]]></desc>\n" +
85     "        <qualifier><![CDATA[APP]]></qualifier>\n" +
86     "        <p>proj1-key</p>\n" +
87     "        <p>proj2-key</p>\n" +
88     "        <branch key=\"app1-branch1\">\n" +
89     "            <p branch=\"proj1-branch-1\">proj1-key</p>\n" +
90     "            <p branch=\"m1\">proj2-key</p>\n" +
91     "        </branch>\n" +
92     "        <branch key=\"app1-branch2\">\n" +
93     "            <p branch=\"proj1-branch-2\">proj1-key</p>\n" +
94     "            <p branch=\"m1\">proj2-key</p>\n" +
95     "        </branch>\n" +
96     "    </vw>\n" +
97     "    <vw key=\"app2-key\" def=\"false\">\n" +
98     "        <name><![CDATA[app2-key]]></name>\n" +
99     "        <desc><![CDATA[]]></desc>\n" +
100     "        <qualifier><![CDATA[APP]]></qualifier>\n" +
101     "        <p>proj1-key</p>\n" +
102     "        <p>proj2-key</p>\n" +
103     "        <branch key=\"m1\">\n" +
104     "            <p branch=\"proj1-branch-1\">proj1-key</p>\n" +
105     "            <p branch=\"m1\">proj2-key</p>\n" +
106     "        </branch>\n" +
107     "    </vw>\n" +
108     "    <vw key=\"port1\" def=\"true\">\n" +
109     "        <name><![CDATA[port1]]></name>\n" +
110     "        <desc><![CDATA[]]></desc>\n" +
111     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
112     "    </vw>\n" +
113     "    <vw key=\"port2\" def=\"false\">\n" +
114     "        <name><![CDATA[port2]]></name>\n" +
115     "        <desc><![CDATA[]]></desc>\n" +
116     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
117     "        <vw-ref><![CDATA[app1-key]]></vw-ref>\n" +
118     "        <vw-ref><![CDATA[port1]]></vw-ref>\n" +
119     "    </vw>\n" +
120     "    <vw key=\"port3\" def=\"false\">\n" +
121     "        <name><![CDATA[port3]]></name>\n" +
122     "        <desc><![CDATA[]]></desc>\n" +
123     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
124     "        <p>proj1-key</p>\n" +
125     "    </vw>\n" +
126     "    <vw key=\"port4\" def=\"false\">\n" +
127     "        <name><![CDATA[port4]]></name>\n" +
128     "        <desc><![CDATA[]]></desc>\n" +
129     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
130     "    </vw>\n" +
131     "    <vw key=\"portx\" def=\"false\" root=\"port2\" parent=\"port2\">\n" +
132     "        <name><![CDATA[portx]]></name>\n" +
133     "    </vw>\n" +
134     "    <vw key=\"port5\" def=\"false\">\n" +
135     "        <name><![CDATA[port5]]></name>\n" +
136     "        <desc><![CDATA[]]></desc>\n" +
137     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
138     "        <tagsAssociation>\n" +
139     "            <tag>tag1</tag>\n" +
140     "        </tagsAssociation>\n" +
141     "    </vw>\n" +
142     "    <vw key=\"port6\" def=\"false\">\n" +
143     "        <name><![CDATA[port6]]></name>\n" +
144     "        <desc><![CDATA[]]></desc>\n" +
145     "        <regexp><![CDATA[.*oj.*]]></regexp>\n" +
146     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
147     "    </vw>\n" +
148     "    <vw key=\"port7\" def=\"false\">\n" +
149     "        <name><![CDATA[port7]]></name>\n" +
150     "        <desc><![CDATA[]]></desc>\n" +
151     "        <tag_key><![CDATA[business_value]]></tag_key>\n" +
152     "        <tag_value><![CDATA[12]]></tag_value>\n" +
153     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
154     "    </vw>\n" +
155     "</views>";
156
157   private static final String COMPLEX_XML_AFTER = "<views>\n" +
158     "    <vw key=\"port1\" def=\"true\">\n" +
159     "        <name><![CDATA[port1]]></name>\n" +
160     "        <desc><![CDATA[]]></desc>\n" +
161     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
162     "    </vw>\n" +
163     "    <vw key=\"port2\" def=\"false\">\n" +
164     "        <name><![CDATA[port2]]></name>\n" +
165     "        <desc><![CDATA[]]></desc>\n" +
166     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
167     "        <vw-ref><![CDATA[app1-key]]></vw-ref>\n" +
168     "        <vw-ref><![CDATA[port1]]></vw-ref>\n" +
169     "    </vw>\n" +
170     "    <vw key=\"port3\" def=\"false\">\n" +
171     "        <name><![CDATA[port3]]></name>\n" +
172     "        <desc><![CDATA[]]></desc>\n" +
173     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
174     "        <p>proj1-key</p>\n" +
175     "    </vw>\n" +
176     "    <vw key=\"port4\" def=\"false\">\n" +
177     "        <name><![CDATA[port4]]></name>\n" +
178     "        <desc><![CDATA[]]></desc>\n" +
179     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
180     "    </vw>\n" +
181     "    <vw key=\"portx\" def=\"false\" root=\"port2\" parent=\"port2\">\n" +
182     "        <name><![CDATA[portx]]></name>\n" +
183     "    </vw>\n" +
184     "    <vw key=\"port5\" def=\"false\">\n" +
185     "        <name><![CDATA[port5]]></name>\n" +
186     "        <desc><![CDATA[]]></desc>\n" +
187     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
188     "        <tagsAssociation>\n" +
189     "            <tag>tag1</tag>\n" +
190     "        </tagsAssociation>\n" +
191     "    </vw>\n" +
192     "    <vw key=\"port6\" def=\"false\">\n" +
193     "        <name><![CDATA[port6]]></name>\n" +
194     "        <desc><![CDATA[]]></desc>\n" +
195     "        <regexp><![CDATA[.*oj.*]]></regexp>\n" +
196     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
197     "    </vw>\n" +
198     "    <vw key=\"port7\" def=\"false\">\n" +
199     "        <name><![CDATA[port7]]></name>\n" +
200     "        <desc><![CDATA[]]></desc>\n" +
201     "        <tag_key><![CDATA[business_value]]></tag_key>\n" +
202     "        <tag_value><![CDATA[12]]></tag_value>\n" +
203     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
204     "    </vw>\n" +
205     "</views>";
206
207   private static final String LARGE_XML_BEFORE_AND_AFTER = "<views>\n" +
208     "    <vw key=\"port1\" def=\"true\">\n" +
209     "        <name><![CDATA[port1]]></name>\n" +
210     "        <desc><![CDATA[]]></desc>\n" +
211     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
212     "    </vw>\n" +
213     "    <vw key=\"port2\" def=\"false\">\n" +
214     "        <name><![CDATA[port2]]></name>\n" +
215     "        <desc><![CDATA[]]></desc>\n" +
216     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
217     "        <vw-ref><![CDATA[app1-key]]></vw-ref>\n" +
218     "        <vw-ref><![CDATA[port1]]></vw-ref>\n" +
219     "    </vw>\n" +
220     "    <vw key=\"port3\" def=\"false\">\n" +
221     "        <name><![CDATA[port3]]></name>\n" +
222     "        <desc><![CDATA[]]></desc>\n" +
223     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
224     "        <p>proj1-key</p>\n" +
225     "    </vw>\n" +
226     "    <vw key=\"port4\" def=\"false\">\n" +
227     "        <name><![CDATA[port4]]></name>\n" +
228     "        <desc><![CDATA[]]></desc>\n" +
229     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
230     "    </vw>\n" +
231     "    <vw key=\"portx\" def=\"false\" root=\"port2\" parent=\"port2\">\n" +
232     "        <name><![CDATA[portx]]></name>\n" +
233     "    </vw>\n" +
234     "    <vw key=\"port5\" def=\"false\">\n" +
235     "        <name><![CDATA[port5]]></name>\n" +
236     "        <desc><![CDATA[]]></desc>\n" +
237     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
238     "        <tagsAssociation>\n" +
239     "            <tag>tag1</tag>\n" +
240     "        </tagsAssociation>\n" +
241     "    </vw>\n" +
242     "    <vw key=\"port6\" def=\"false\">\n" +
243     "        <name><![CDATA[port6]]></name>\n" +
244     "        <desc><![CDATA[]]></desc>\n" +
245     "        <regexp><![CDATA[.*oj.*]]></regexp>\n" +
246     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
247     "    </vw>\n" +
248     "    <vw key=\"port7\" def=\"false\">\n" +
249     "        <name><![CDATA[port7]]></name>\n" +
250     "        <desc><![CDATA[]]></desc>\n" +
251     "        <tag_key><![CDATA[business_value]]></tag_key>\n" +
252     "        <tag_value><![CDATA[12]]></tag_value>\n" +
253     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
254     "    </vw>\n" +
255     "    <vw key=\"port8\" def=\"false\">\n" +
256     "        <name><![CDATA[port7]]></name>\n" +
257     "        <desc><![CDATA[]]></desc>\n" +
258     "        <tag_key><![CDATA[business_value]]></tag_key>\n" +
259     "        <tag_value><![CDATA[12]]></tag_value>\n" +
260     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
261     "    </vw>\n" +
262     "    <vw key=\"port9\" def=\"false\">\n" +
263     "        <name><![CDATA[port7]]></name>\n" +
264     "        <desc><![CDATA[]]></desc>\n" +
265     "        <tag_key><![CDATA[business_value]]></tag_key>\n" +
266     "        <tag_value><![CDATA[12]]></tag_value>\n" +
267     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
268     "    </vw>\n" +
269     "    <vw key=\"port10\" def=\"false\">\n" +
270     "        <name><![CDATA[port7]]></name>\n" +
271     "        <desc><![CDATA[]]></desc>\n" +
272     "        <tag_key><![CDATA[business_value]]></tag_key>\n" +
273     "        <tag_value><![CDATA[12]]></tag_value>\n" +
274     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
275     "    </vw>\n" +
276     "    <vw key=\"port11\" def=\"false\">\n" +
277     "        <name><![CDATA[port7]]></name>\n" +
278     "        <desc><![CDATA[]]></desc>\n" +
279     "        <tag_key><![CDATA[business_value]]></tag_key>\n" +
280     "        <tag_value><![CDATA[12]]></tag_value>\n" +
281     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
282     "    </vw>\n" +
283     "    <vw key=\"port12\" def=\"false\">\n" +
284     "        <name><![CDATA[port7]]></name>\n" +
285     "        <desc><![CDATA[]]></desc>\n" +
286     "        <tag_key><![CDATA[business_value]]></tag_key>\n" +
287     "        <tag_value><![CDATA[12]]></tag_value>\n" +
288     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
289     "    </vw>\n" +
290     "    <vw key=\"port13\" def=\"false\">\n" +
291     "        <name><![CDATA[port7]]></name>\n" +
292     "        <desc><![CDATA[]]></desc>\n" +
293     "        <tag_key><![CDATA[business_value]]></tag_key>\n" +
294     "        <tag_value><![CDATA[12]]></tag_value>\n" +
295     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
296     "    </vw>\n" +
297     "    <vw key=\"port14\" def=\"false\">\n" +
298     "        <name><![CDATA[port7]]></name>\n" +
299     "        <desc><![CDATA[]]></desc>\n" +
300     "        <tag_key><![CDATA[business_value]]></tag_key>\n" +
301     "        <tag_value><![CDATA[12]]></tag_value>\n" +
302     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
303     "    </vw>\n" +
304     "    <vw key=\"port15\" def=\"false\">\n" +
305     "        <name><![CDATA[port7]]></name>\n" +
306     "        <desc><![CDATA[]]></desc>\n" +
307     "        <tag_key><![CDATA[business_value]]></tag_key>\n" +
308     "        <tag_value><![CDATA[12]]></tag_value>\n" +
309     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
310     "    </vw>\n" +
311     "    <vw key=\"port16\" def=\"false\">\n" +
312     "        <name><![CDATA[port7]]></name>\n" +
313     "        <desc><![CDATA[]]></desc>\n" +
314     "        <tag_key><![CDATA[business_value]]></tag_key>\n" +
315     "        <tag_value><![CDATA[12]]></tag_value>\n" +
316     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
317     "    </vw>\n" +
318     "    <vw key=\"port17\" def=\"false\">\n" +
319     "        <name><![CDATA[port7]]></name>\n" +
320     "        <desc><![CDATA[]]></desc>\n" +
321     "        <tag_key><![CDATA[business_value]]></tag_key>\n" +
322     "        <tag_value><![CDATA[12]]></tag_value>\n" +
323     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
324     "    </vw>\n" +
325     "    <vw key=\"port18\" def=\"false\">\n" +
326     "        <name><![CDATA[port7]]></name>\n" +
327     "        <desc><![CDATA[]]></desc>\n" +
328     "        <tag_key><![CDATA[business_value]]></tag_key>\n" +
329     "        <tag_value><![CDATA[12]]></tag_value>\n" +
330     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
331     "    </vw>\n" +
332     "    <vw key=\"port19\" def=\"false\">\n" +
333     "        <name><![CDATA[port7]]></name>\n" +
334     "        <desc><![CDATA[]]></desc>\n" +
335     "        <tag_key><![CDATA[business_value]]></tag_key>\n" +
336     "        <tag_value><![CDATA[12]]></tag_value>\n" +
337     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
338     "    </vw>\n" +
339     "    <vw key=\"port20\" def=\"false\">\n" +
340     "        <name><![CDATA[port7]]></name>\n" +
341     "        <desc><![CDATA[]]></desc>\n" +
342     "        <tag_key><![CDATA[business_value]]></tag_key>\n" +
343     "        <tag_value><![CDATA[12]]></tag_value>\n" +
344     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
345     "    </vw>\n" +
346     "    <vw key=\"port21\" def=\"false\">\n" +
347     "        <name><![CDATA[port7]]></name>\n" +
348     "        <desc><![CDATA[]]></desc>\n" +
349     "        <tag_key><![CDATA[business_value]]></tag_key>\n" +
350     "        <tag_value><![CDATA[12]]></tag_value>\n" +
351     "        <qualifier><![CDATA[VW]]></qualifier>\n" +
352     "    </vw>\n" +
353     "</views>";
354
355   @Rule
356   public CoreDbTester db = CoreDbTester.createForSchema(MigrateApplicationDefinitionsFromXmlToDbTest.class, "schema.sql");
357
358   private final UuidFactory uuidFactory = UuidFactoryFast.getInstance();
359   private final System2 system2 = new TestSystem2().setNow(NOW);
360   private final MigrationStep underTest = new MigrateApplicationDefinitionsFromXmlToDb(db.database(), uuidFactory, system2);
361
362   @Test
363   public void does_nothing_when_no_views_def_property() throws SQLException {
364     setupProjectsAndApps();
365
366     underTest.execute();
367
368     assertThat(db.countSql("select count(*) from app_projects")).isZero();
369     assertThat(db.countSql("select count(*) from app_branch_project_branch")).isZero();
370     assertThat(db.countSql("select count(*) from internal_properties where kee='views.def'")).isZero();
371   }
372
373   @Test
374   public void does_nothing_when_views_def_property_empty_string() throws SQLException {
375     setupProjectsAndApps();
376     insertViewsDefInternalProperty("");
377
378     underTest.execute();
379
380     assertThat(db.countSql("select count(*) from app_projects")).isZero();
381     assertThat(db.countSql("select count(*) from app_branch_project_branch")).isZero();
382   }
383
384   @Test
385   public void does_nothing_when_views_def_property_empty_views_content() throws SQLException {
386     setupProjectsAndApps();
387     insertViewsDefInternalProperty(EMPTY_XML);
388
389     underTest.execute();
390
391     assertThat(db.countSql("select count(*) from app_projects")).isZero();
392     assertThat(db.countSql("select count(*) from app_branch_project_branch")).isZero();
393   }
394
395   @Test
396   public void throws_ISE_when_views_def_property_does_not_pass_validation() {
397     setupProjectsAndApps();
398     insertViewsDefInternalProperty("abcdefghi");
399
400     assertThatThrownBy(underTest::execute)
401       .isInstanceOf(IllegalStateException.class)
402       .hasMessage("Failed to migrate views definitions property.");
403
404     assertThat(db.countSql("select count(*) from app_projects")).isZero();
405     assertThat(db.countSql("select count(*) from app_branch_project_branch")).isZero();
406   }
407
408   @Test
409   public void migrates_applications_to_new_tables() throws SQLException {
410     setupProjectsAndApps();
411     insertViewsDefInternalProperty(COMPLEX_XML_BEFORE);
412
413     underTest.execute();
414
415     assertThat(db.select("select uuid from projects"))
416       .extracting(r -> r.get("UUID"))
417       .containsExactlyInAnyOrder(PROJECT_1_UUID, PROJECT_2_UUID, APP_1_UUID, APP_2_UUID);
418     assertThat(db.select("select application_uuid, project_uuid from app_projects"))
419       .extracting(r -> r.get("APPLICATION_UUID"), r -> r.get("PROJECT_UUID"))
420       .containsExactlyInAnyOrder(
421         tuple(APP_1_UUID, PROJECT_1_UUID),
422         tuple(APP_1_UUID, PROJECT_2_UUID),
423         tuple(APP_2_UUID, PROJECT_1_UUID),
424         tuple(APP_2_UUID, PROJECT_2_UUID));
425     assertThat(db.select("select application_uuid, project_uuid, application_branch_uuid, project_branch_uuid from app_branch_project_branch"))
426       .extracting(r -> r.get("APPLICATION_UUID"), r -> r.get("PROJECT_UUID"), r -> r.get("APPLICATION_BRANCH_UUID"), r -> r.get("PROJECT_BRANCH_UUID"))
427       .containsExactlyInAnyOrder(
428         tuple(APP_1_UUID, PROJECT_1_UUID, APP_1_BRANCH_1_UUID, PROJECT_1_BRANCH_1_UUID),
429         tuple(APP_1_UUID, PROJECT_2_UUID, APP_1_BRANCH_1_UUID, PROJECT_2_BRANCH_1_UUID),
430         tuple(APP_1_UUID, PROJECT_1_UUID, APP_1_BRANCH_2_UUID, PROJECT_1_BRANCH_2_UUID),
431         tuple(APP_1_UUID, PROJECT_2_UUID, APP_1_BRANCH_2_UUID, PROJECT_2_BRANCH_1_UUID),
432         tuple(APP_2_UUID, PROJECT_1_UUID, APP_2_BRANCH_1_UUID, PROJECT_1_BRANCH_1_UUID),
433         tuple(APP_2_UUID, PROJECT_2_UUID, APP_2_BRANCH_1_UUID, PROJECT_2_BRANCH_1_UUID));
434   }
435
436   @Test
437   public void migrates_applications_without_application_branches_to_new_tables() throws SQLException {
438     setupFullProject1();
439     setupProject2();
440     setupApp1WithNoBranches();
441     insertViewsDefInternalProperty(APP_WITH_NO_BRANCHES_XML);
442
443     underTest.execute();
444
445     assertThat(db.select("select uuid from projects"))
446       .extracting(r -> r.get("UUID"))
447       .containsExactlyInAnyOrder(PROJECT_1_UUID, PROJECT_2_UUID, APP_1_UUID);
448     assertThat(db.select("select application_uuid, project_uuid from app_projects"))
449       .extracting(r -> r.get("APPLICATION_UUID"), r -> r.get("PROJECT_UUID"))
450       .containsExactlyInAnyOrder(
451         tuple(APP_1_UUID, PROJECT_1_UUID),
452         tuple(APP_1_UUID, PROJECT_2_UUID));
453     assertThat(db.countSql("select count(*) from app_branch_project_branch")).isZero();
454   }
455
456   @Test
457   public void migrates_app_with_0_projects_in_views_definition() throws SQLException {
458     setupProjectsAndApps();
459     insertViewsDefInternalProperty(EMPTY_APP_XML);
460
461     underTest.execute();
462
463     assertThat(db.select("select uuid from projects"))
464       .extracting(r -> r.get("UUID"))
465       .containsExactlyInAnyOrder(PROJECT_1_UUID, PROJECT_2_UUID, APP_1_UUID, APP_2_UUID);
466     assertThat(db.countSql("select count(*) from app_projects")).isZero();
467     assertThat(db.countSql("select count(*) from app_branch_project_branch")).isZero();
468   }
469
470   @Test
471   public void skips_apps_that_are_present_in_views_definition_but_not_in_db() throws SQLException {
472     setupFullProject1();
473     setupProject2();
474     setupApp1WithTwoBranches();
475     insertViewsDefInternalProperty(COMPLEX_XML_BEFORE);
476
477     underTest.execute();
478
479     assertThat(db.select("select uuid from projects"))
480       .extracting(r -> r.get("UUID"))
481       .containsExactlyInAnyOrder(PROJECT_1_UUID, PROJECT_2_UUID, APP_1_UUID);
482     assertThat(db.select("select application_uuid, project_uuid from app_projects"))
483       .extracting(r -> r.get("APPLICATION_UUID"), r -> r.get("PROJECT_UUID"))
484       .containsExactlyInAnyOrder(
485         tuple(APP_1_UUID, PROJECT_1_UUID),
486         tuple(APP_1_UUID, PROJECT_2_UUID));
487     assertThat(db.select("select application_uuid, project_uuid, application_branch_uuid, project_branch_uuid from app_branch_project_branch"))
488       .extracting(r -> r.get("APPLICATION_UUID"), r -> r.get("PROJECT_UUID"), r -> r.get("APPLICATION_BRANCH_UUID"), r -> r.get("PROJECT_BRANCH_UUID"))
489       .containsExactlyInAnyOrder(
490         tuple(APP_1_UUID, PROJECT_1_UUID, APP_1_BRANCH_1_UUID, PROJECT_1_BRANCH_1_UUID),
491         tuple(APP_1_UUID, PROJECT_2_UUID, APP_1_BRANCH_1_UUID, PROJECT_2_BRANCH_1_UUID),
492         tuple(APP_1_UUID, PROJECT_1_UUID, APP_1_BRANCH_2_UUID, PROJECT_1_BRANCH_2_UUID),
493         tuple(APP_1_UUID, PROJECT_2_UUID, APP_1_BRANCH_2_UUID, PROJECT_2_BRANCH_1_UUID));
494   }
495
496   @Test
497   public void skips_app_branches_that_are_present_in_views_definition_but_not_in_db() throws SQLException {
498     setupFullProject1();
499     setupProject2();
500     setupApp1WithNoBranches();
501     setupApp2();
502     insertViewsDefInternalProperty(COMPLEX_XML_BEFORE);
503
504     underTest.execute();
505
506     assertThat(db.select("select uuid from projects"))
507       .extracting(r -> r.get("UUID"))
508       .containsExactlyInAnyOrder(PROJECT_1_UUID, PROJECT_2_UUID, APP_1_UUID, APP_2_UUID);
509     assertThat(db.select("select application_uuid, project_uuid from app_projects"))
510       .extracting(r -> r.get("APPLICATION_UUID"), r -> r.get("PROJECT_UUID"))
511       .containsExactlyInAnyOrder(
512         tuple(APP_1_UUID, PROJECT_1_UUID),
513         tuple(APP_1_UUID, PROJECT_2_UUID),
514         tuple(APP_2_UUID, PROJECT_1_UUID),
515         tuple(APP_2_UUID, PROJECT_2_UUID));
516     assertThat(db.select("select application_uuid, project_uuid, application_branch_uuid, project_branch_uuid from app_branch_project_branch"))
517       .extracting(r -> r.get("APPLICATION_UUID"), r -> r.get("PROJECT_UUID"), r -> r.get("APPLICATION_BRANCH_UUID"), r -> r.get("PROJECT_BRANCH_UUID"))
518       .containsExactlyInAnyOrder(
519         tuple(APP_2_UUID, PROJECT_1_UUID, APP_2_BRANCH_1_UUID, PROJECT_1_BRANCH_1_UUID),
520         tuple(APP_2_UUID, PROJECT_2_UUID, APP_2_BRANCH_1_UUID, PROJECT_2_BRANCH_1_UUID));
521   }
522
523   @Test
524   public void skips_projects_that_are_present_in_apps_views_definitions_but_not_in_db() throws SQLException {
525     setupPartialProject1();
526     setupApp1WithTwoBranches();
527     setupApp2();
528     insertViewsDefInternalProperty(COMPLEX_XML_BEFORE);
529
530     underTest.execute();
531
532     assertThat(db.select("select uuid from projects"))
533       .extracting(r -> r.get("UUID"))
534       .containsExactlyInAnyOrder(PROJECT_1_UUID, APP_1_UUID, APP_2_UUID);
535     assertThat(db.select("select application_uuid, project_uuid from app_projects"))
536       .extracting(r -> r.get("APPLICATION_UUID"), r -> r.get("PROJECT_UUID"))
537       .containsExactlyInAnyOrder(
538         tuple(APP_1_UUID, PROJECT_1_UUID),
539         tuple(APP_2_UUID, PROJECT_1_UUID));
540     assertThat(db.select("select application_uuid, project_uuid, application_branch_uuid, project_branch_uuid from app_branch_project_branch"))
541       .extracting(r -> r.get("APPLICATION_UUID"), r -> r.get("PROJECT_UUID"), r -> r.get("APPLICATION_BRANCH_UUID"), r -> r.get("PROJECT_BRANCH_UUID"))
542       .containsExactlyInAnyOrder(
543         tuple(APP_1_UUID, PROJECT_1_UUID, APP_1_BRANCH_1_UUID, PROJECT_1_BRANCH_1_UUID),
544         tuple(APP_2_UUID, PROJECT_1_UUID, APP_2_BRANCH_1_UUID, PROJECT_1_BRANCH_1_UUID));
545   }
546
547   @Test
548   public void skips_projects_branches_that_are_present_in_apps_views_definitions_but_not_in_db() throws SQLException {
549     setupPartialProject1();
550     setupProject2();
551     setupApp1WithTwoBranches();
552     setupApp2();
553     insertViewsDefInternalProperty(COMPLEX_XML_BEFORE);
554
555     underTest.execute();
556
557     assertThat(db.select("select uuid from projects"))
558       .extracting(r -> r.get("UUID"))
559       .containsExactlyInAnyOrder(PROJECT_1_UUID, PROJECT_2_UUID, APP_1_UUID, APP_2_UUID);
560     assertThat(db.select("select application_uuid, project_uuid from app_projects"))
561       .extracting(r -> r.get("APPLICATION_UUID"), r -> r.get("PROJECT_UUID"))
562       .containsExactlyInAnyOrder(
563         tuple(APP_1_UUID, PROJECT_1_UUID),
564         tuple(APP_1_UUID, PROJECT_2_UUID),
565         tuple(APP_2_UUID, PROJECT_1_UUID),
566         tuple(APP_2_UUID, PROJECT_2_UUID));
567     assertThat(db.select("select application_uuid, project_uuid, application_branch_uuid, project_branch_uuid from app_branch_project_branch"))
568       .extracting(r -> r.get("APPLICATION_UUID"), r -> r.get("PROJECT_UUID"), r -> r.get("APPLICATION_BRANCH_UUID"), r -> r.get("PROJECT_BRANCH_UUID"))
569       .containsExactlyInAnyOrder(
570         tuple(APP_1_UUID, PROJECT_1_UUID, APP_1_BRANCH_1_UUID, PROJECT_1_BRANCH_1_UUID),
571         tuple(APP_1_UUID, PROJECT_2_UUID, APP_1_BRANCH_1_UUID, PROJECT_2_BRANCH_1_UUID),
572         tuple(APP_1_UUID, PROJECT_2_UUID, APP_1_BRANCH_2_UUID, PROJECT_2_BRANCH_1_UUID),
573         tuple(APP_2_UUID, PROJECT_1_UUID, APP_2_BRANCH_1_UUID, PROJECT_1_BRANCH_1_UUID),
574         tuple(APP_2_UUID, PROJECT_2_UUID, APP_2_BRANCH_1_UUID, PROJECT_2_BRANCH_1_UUID));
575   }
576
577   @Test
578   public void removes_application_definitions_from_xml() throws SQLException {
579     setupProjectsAndApps();
580     insertViewsDefInternalProperty(COMPLEX_XML_BEFORE);
581
582     underTest.execute();
583
584     assertThat(db.countSql("select count(*) from internal_properties where kee='views.def'")).isOne();
585     assertViewsXmlDefinitionSimilar(COMPLEX_XML_AFTER, false);
586   }
587
588   @Test
589   public void removes_application_definitions_from_large_xmls() throws SQLException {
590     setupProjectsAndApps();
591     insertViewsDefInternalProperty(LARGE_XML_BEFORE_AND_AFTER);
592
593     underTest.execute();
594
595     assertThat(db.countSql("select count(*) from internal_properties where kee='views.def'")).isOne();
596     assertViewsXmlDefinitionSimilar(LARGE_XML_BEFORE_AND_AFTER, true);
597   }
598
599   @Test
600   public void does_not_change_the_xml_if_there_are_no_application_definitions() throws SQLException {
601     setupProjectsAndApps();
602     insertViewsDefInternalProperty(EMPTY_XML);
603
604     underTest.execute();
605
606     assertThat(db.countSql("select count(*) from internal_properties where kee='views.def'")).isOne();
607     assertViewsXmlDefinitionSimilar(EMPTY_XML, false);
608   }
609
610   private void setupProjectsAndApps() {
611     setupFullProject1();
612     setupProject2();
613     setupApp1WithTwoBranches();
614     setupApp2();
615   }
616
617   private void setupFullProject1() {
618     setupPartialProject1();
619     insertBranch(PROJECT_1_BRANCH_2_UUID, PROJECT_1_UUID, "proj1-branch-2");
620   }
621
622   private void setupPartialProject1() {
623     insertProject(PROJECT_1_UUID, "proj1-key", QUALIFIER_PROJECT);
624     insertBranch(PROJECT_1_MASTER_BRANCH_UUID, PROJECT_1_UUID, "master");
625     insertBranch(PROJECT_1_BRANCH_1_UUID, PROJECT_1_UUID, "proj1-branch-1");
626   }
627
628   private void setupProject2() {
629     insertProject(PROJECT_2_UUID, "proj2-key", QUALIFIER_PROJECT);
630     insertBranch(PROJECT_2_MASTER_BRANCH_UUID, PROJECT_2_UUID, "master");
631     insertBranch(PROJECT_2_BRANCH_1_UUID, PROJECT_2_UUID, "m1");
632   }
633
634   private void setupApp1WithNoBranches() {
635     insertProject(APP_1_UUID, "app1-key", QUALIFIER_APP);
636     insertBranch(APP_1_MASTER_BRANCH_UUID, APP_1_UUID, "master");
637   }
638
639   private void setupApp1WithOneBranch() {
640     setupApp1WithNoBranches();
641     insertBranch(APP_1_BRANCH_1_UUID, APP_1_UUID, "app1-branch1");
642   }
643
644   private void setupApp1WithTwoBranches() {
645     setupApp1WithOneBranch();
646     insertBranch(APP_1_BRANCH_2_UUID, APP_1_UUID, "app1-branch2");
647   }
648
649   private void setupApp2() {
650     insertProject(APP_2_UUID, "app2-key", QUALIFIER_APP);
651     insertBranch(APP_2_MASTER_BRANCH_UUID, APP_2_UUID, "master");
652     insertBranch(APP_2_BRANCH_1_UUID, APP_2_UUID, "m1");
653   }
654
655   private void insertViewsDefInternalProperty(@Nullable String xml) {
656     String valueColumn = "text_value";
657     if (xml != null && xml.length() > TEXT_VALUE_MAX_LENGTH) {
658       valueColumn = "clob_value";
659     }
660
661     db.executeInsert("internal_properties",
662       "kee", "views.def",
663       "is_empty", "false",
664       valueColumn, xml,
665       "created_at", system2.now());
666   }
667
668   private void insertProject(String uuid, String key, String qualifier) {
669     db.executeInsert("PROJECTS",
670       "UUID", uuid,
671       "KEE", key,
672       "QUALIFIER", qualifier,
673       "ORGANIZATION_UUID", uuid + "-key",
674       "TAGS", "tag1",
675       "PRIVATE", Boolean.toString(false),
676       "UPDATED_AT", System2.INSTANCE.now());
677   }
678
679   private void insertBranch(String uuid, String projectUuid, String key) {
680     db.executeInsert(
681       "PROJECT_BRANCHES",
682       "UUID", uuid,
683       "PROJECT_UUID", projectUuid,
684       "KEE", key,
685       "BRANCH_TYPE", "BRANCH",
686       "MERGE_BRANCH_UUID", null,
687       "CREATED_AT", System2.INSTANCE.now(),
688       "UPDATED_AT", System2.INSTANCE.now(),
689       "NEED_ISSUE_SYNC", Boolean.toString(false));
690   }
691
692   private void assertViewsXmlDefinitionSimilar(final String expectedValue, final boolean expectClob) {
693     Map<String, Object> result = db.selectFirst("select text_value, clob_value from internal_properties where kee='views.def'");
694     String textValue = (String) result.get("TEXT_VALUE");
695     String clobValue = (String) result.get("CLOB_VALUE");
696
697     String existingValue;
698     if (expectClob) {
699       existingValue = clobValue;
700       assertThat(textValue).isNull();
701     } else {
702       existingValue = textValue;
703       assertThat(clobValue).isNull();
704     }
705
706     Diff diff = DiffBuilder
707       .compare(Input.fromString(expectedValue))
708       .withTest(Input.fromString(existingValue))
709       .ignoreWhitespace()
710       .ignoreComments()
711       .checkForSimilar()
712       .build();
713     assertThat(diff.getDifferences())
714       .as(expectedValue)
715       .isEmpty();
716   }
717 }