[ACS-1455] Schema validation report expansion with patch specific problems told apart (#386)

* Update schema validation report

- Introduced changes to tell apart problems due to optional unapplied
  patches within schema validation report

* Update SchemaDifferenceHelper

- Now populating optionalUpgradePatches list during the schema bootstrap
  registration to conform with patch registration mechanising already in use
- Added tests

* Make minor corrections

Co-authored-by: Nana Insaidoo <nana.insaidoo@meterian.com>
This commit is contained in:
Nana Insaidoo
2021-04-27 09:57:47 +01:00
committed by GitHub
parent 6c9b05d289
commit 5ce179f209
15 changed files with 3482 additions and 19 deletions

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2020 Alfresco Software Limited
* Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -233,7 +233,9 @@ import org.junit.runners.Suite;
org.alfresco.repo.rendition2.TransformationOptionsConverterTest.class,
org.alfresco.transform.client.registry.TransformServiceRegistryConfigTest.class,
org.alfresco.repo.event2.RepoEvent2UnitSuite.class
org.alfresco.repo.event2.RepoEvent2UnitSuite.class,
org.alfresco.util.schemacomp.SchemaDifferenceHelperUnitTest.class
})
public class AllUnitTestsSuite
{

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2017 Alfresco Software Limited
* Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -85,6 +85,7 @@ import org.junit.runners.Suite;
org.alfresco.util.schemacomp.DbToXMLTest.class,
org.alfresco.util.schemacomp.ExportDbTest.class,
org.alfresco.util.schemacomp.SchemaReferenceFileTest.class,
org.alfresco.util.schemacomp.SchemaBootstrapTest.class,
org.alfresco.repo.module.ModuleComponentHelperTest.class,
org.alfresco.repo.node.getchildren.GetChildrenCannedQueryTest.class,

View File

@@ -0,0 +1,86 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.util.schemacomp;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.List;
import org.alfresco.repo.admin.patch.impl.SchemaUpgradeScriptPatch;
import org.alfresco.repo.domain.schema.SchemaBootstrap;
import org.alfresco.test_category.OwnJVMTestsCategory;
import org.alfresco.util.test.junitrules.ApplicationContextInit;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.rules.RuleChain;
@Category({OwnJVMTestsCategory.class})
public class SchemaBootstrapTest
{
private static final String BOOTSTRAP_TEST_CONTEXT = "classpath*:alfresco/dbscripts/test-bootstrap-context.xml";
private static final List<String> TEST_SCHEMA_REFERENCE_URLS = Arrays.asList(
"classpath:alfresco/dbscripts/create/${db.script.dialect}/Test-Schema-Reference-ALF.xml",
"classpath:alfresco/dbscripts/create/${db.script.dialect}/Schema-Reference-ACT.xml");
private static ApplicationContextInit APP_CONTEXT_INIT = ApplicationContextInit.createStandardContextWithOverrides(BOOTSTRAP_TEST_CONTEXT);
@ClassRule
public static RuleChain staticRuleChain = RuleChain.outerRule(APP_CONTEXT_INIT);
private SchemaBootstrap schemaBootstrap;
private SchemaUpgradeScriptPatch optionalPatch;
@Before
public void setUp() throws Exception
{
schemaBootstrap = (SchemaBootstrap) APP_CONTEXT_INIT.getApplicationContext().getBean("schemaBootstrap");
schemaBootstrap.setSchemaReferenceUrls(TEST_SCHEMA_REFERENCE_URLS);
optionalPatch = (SchemaUpgradeScriptPatch) APP_CONTEXT_INIT.getApplicationContext().getBean("patchDbVOAddIndexTest");
}
@Test
public void shouldSchemaValidationReportProblemsCausedByUnappliedOptionalPatch()
{
ByteArrayOutputStream buff = new ByteArrayOutputStream();
PrintWriter out = new PrintWriter(buff);
int numProblems = schemaBootstrap.validateSchema(null, out);
out.flush();
assertEquals(1, numProblems);
String problems = buff.toString();
assertTrue("Missing optional patch-specific problems report: \n" + problems,
problems.contains("The following problems will be resolved once the long running patch "
+ optionalPatch.getId() + " has been run"));
}
}

View File

@@ -0,0 +1,207 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.util.schemacomp;
import static org.alfresco.util.schemacomp.Difference.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import org.alfresco.repo.admin.patch.AppliedPatch;
import org.alfresco.repo.admin.patch.PatchService;
import org.alfresco.repo.admin.patch.impl.SchemaUpgradeScriptPatch;
import org.alfresco.repo.domain.dialect.Dialect;
import org.alfresco.util.schemacomp.model.Index;
import org.alfresco.util.schemacomp.model.Schema;
import org.alfresco.util.schemacomp.model.Table;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
public class SchemaDifferenceHelperUnitTest
{
private static final String TEST_PATCH_ID = "patch.db-V1.0-test";
private static final String BASE_PROBLEM_PATTERN = ".*missing %s.*%s";
private SchemaDifferenceHelper differenceHelper;
private PatchService patchService;
private Dialect dialect;
@Rule
public TemporaryFolder testFolder = new TemporaryFolder();
@Before
public void setup()
{
dialect = mock(Dialect.class);
patchService = mock(PatchService.class);
}
@Test
public void shouldNotFindPatchWhenThereAreNoUpgradePatches()
{
Difference diff = createDifference();
differenceHelper = createHelper(Arrays.asList());
assertNull(differenceHelper.findPatchCausingDifference(diff));
}
@Test
public void shouldNotFindPatchWhenUpgradePatchHasBeenApplied() throws IOException
{
Difference diff = createDifference();
SchemaUpgradeScriptPatch upgradeScript = createUpgradeScript(TEST_PATCH_ID);
when(patchService.getPatch(TEST_PATCH_ID)).thenReturn(new AppliedPatch());
differenceHelper = createHelper(Arrays.asList(upgradeScript));
String result = differenceHelper.findPatchCausingDifference(diff);
assertNull(result);
}
@Test
public void shouldNotFindPatchWhenUpgradePatchDoesNotProvideAnyProblemPatternsFile() throws IOException
{
Difference diff = createDifference();
SchemaUpgradeScriptPatch upgradeScript = createUpgradeScript(TEST_PATCH_ID);
upgradeScript.setProblemsPatternFileUrl(null);
when(patchService.getPatch(TEST_PATCH_ID)).thenReturn(null);
differenceHelper = createHelper(Arrays.asList(upgradeScript));
String result = differenceHelper.findPatchCausingDifference(diff);
assertNull(result);
}
@Test
public void shouldFindPatchWhenDifferenceCausedByUpgradePatchIsDetected() throws IOException
{
Index index = createTableIndex("alf_node");
Difference diff = new Difference(Where.ONLY_IN_REFERENCE, new DbProperty(index), null);
SchemaUpgradeScriptPatch upgradeScript = createUpgradeScript(TEST_PATCH_ID,
String.format(BASE_PROBLEM_PATTERN, index.getTypeName(), "idx_alf_node_test"));
when(patchService.getPatch(TEST_PATCH_ID)).thenReturn(null);
differenceHelper = createHelper(Arrays.asList(upgradeScript));
String result = differenceHelper.findPatchCausingDifference(diff);
assertEquals(TEST_PATCH_ID, result);
}
private Difference createDifference()
{
Difference difference = new Difference(Where.IN_BOTH_BUT_DIFFERENCE, mock(DbProperty.class), mock(DbProperty.class));
return difference;
}
private Index createTableIndex(String tableName)
{
Table table = new Table(tableName);
table.setParent(new Schema(""));
return new Index(table, "idx_alf_node_test", Arrays.asList("col_a", "col_b"));
}
private SchemaUpgradeScriptPatch createUpgradeScript(String id, String problemPattern) throws IOException
{
SchemaUpgradeScriptPatch upgradeScript = new SchemaUpgradeScriptPatch();
upgradeScript.setId(id);
Path file = createTempFile(problemPattern);
upgradeScript.setProblemsPatternFileUrl(file.toAbsolutePath().toString());
return upgradeScript;
}
private SchemaUpgradeScriptPatch createUpgradeScript(String id) throws IOException
{
return createUpgradeScript(id, "");
}
private Path createTempFile() throws IOException
{
return Files.createTempFile(testFolder.getRoot().toPath(), null, "txt");
}
private Path createTempFile(String content) throws IOException
{
Path tempFile = createTempFile();
Files.write(tempFile, content.getBytes(StandardCharsets.UTF_8));
return tempFile;
}
private SchemaDifferenceHelper createHelper(List<SchemaUpgradeScriptPatch> upgradePatches)
{
return new SchemaDifferenceHelper(dialect, patchService, upgradePatches) {
@Override
protected String describe(Difference difference)
{
if (difference.getLeft() == null)
{
return String.format("Difference: unexpected %s found in database with path: %s",
difference.getRight().getDbObject().getTypeName(),
difference.getRight().getPath());
}
if(difference.getRight() == null)
{
return String.format("Difference: missing %s from database, expected at path: %s",
difference.getLeft().getDbObject().getTypeName(),
difference.getLeft().getPath());
}
return String.format("Difference: expected %s %s=\"%s\", but was %s=\"%s\"",
difference.getLeft().getDbObject().getTypeName(),
difference.getLeft().getPath(),
difference.getLeft().getPropertyValue(),
difference.getRight().getPath(),
difference.getRight().getPropertyValue());
}
@Override
protected Resource getDialectResource(String resourceUrl)
{
try
{
return new InputStreamResource(new FileInputStream(resourceUrl));
}
catch (Exception e)
{
return null;
}
}
};
}
}

View File

@@ -0,0 +1,60 @@
<?xml version='1.0' encoding='UTF-8'?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-2.5.xsd">
<bean id="schema.upgrade.core" class="org.alfresco.repo.domain.schema.SchemaBootstrapRegistration" init-method="register">
<property name="schemaBootstrap" ref="schemaBootstrap" />
<property name="differenceHelper" ref="differenceHelper" />
<property name="preUpdateScriptPatches">
<list>
<ref bean="patch.db-V4.1-update-activiti-nullable-columns" />
</list>
</property>
<property name="postUpdateScriptPatches">
<list>
<ref bean="patch.db-V4.0-SolrTracking" />
<ref bean="patch.db-V4.0-AclChangeSet2" />
<ref bean="patch.db-V4.0-TenantTables" />
<ref bean="patch.db-V4.1-NodeDeleted" />
<ref bean="patch.db-V4.1-drop-alfqname-fk-indexes" />
<ref bean="patch.db-V4.2-remove-index-acl_id" />
<ref bean="patch.db-V4.1-drop-activiti-feed-format" />
<ref bean="patch.db-V4.2-metadata-query-indexes" />
<ref bean="patch.db-V4.1-fix-Repo-seqs-order" />
<ref bean="patch.db-V4.1-ChildAssoc-OrderBy" />
<ref bean="patch.db-V4.1-createIdxAlfNodeTQN" />
<ref bean="patch.db-V4.2-restructure-idx_alf_nprop_s-MSSQL" />
<ref bean="patch.db-V4.2-migrate-locale-multilingual" />
<ref bean="patch.db-V4.1-AuthorizationTables" />
<ref bean="patch.db-V5.0-ContentUrlEncryptionTables" />
<ref bean="patch.db-V5.1-metadata-query-indexes" />
<ref bean="patch.db-V5.2-remove-jbpm-tables-from-db" />
<ref bean="patch.db-V6.0-change-set-indexes" />
<ref bean="patch.db-V6.3-add-indexes-node-transaction" />
<ref bean="patch.db-V0-add-index-test" />
</list>
</property>
</bean>
<alias name="patch.db-V0-add-index-test" alias="patchDbVOAddIndexTest" />
<bean id="patch.db-V0-add-index-test" class="org.alfresco.repo.admin.patch.impl.SchemaUpgradeScriptPatch" parent="basePatch">
<property name="id"><value>patch.db-V0-add-index-test</value></property>
<property name="description"><value>patch.db-V0-add-index-test.description</value></property>
<property name="fixesFromSchema"><value>0</value></property>
<property name="fixesToSchema"><value>15000</value></property>
<property name="targetSchema"><value>15001</value></property>
<property name="ignored"><value>${system.new-node-transaction-indexes.ignored}</value></property>
<property name="scriptUrl">
<value>classpath:alfresco/dbscripts/upgrade/0/${db.script.dialect}/add-index-test.sql</value>
</property>
<property name="problemsPatternFileUrl">
<value>classpath:alfresco/dbscripts/upgrade/0/${db.script.dialect}/add-index-test-problem-patterns.txt</value>
</property>
</bean>
</beans>

View File

@@ -0,0 +1,15 @@
CREATE INDEX idx_alf_node_test ON alf_node (acl_id, audit_creator); --(optional)
--
-- Record script finish
--
DELETE FROM alf_applied_patch WHERE id = 'patch.db-V0-add-index-test';
INSERT INTO alf_applied_patch
(id, description, fixes_from_schema, fixes_to_schema, applied_to_schema, target_schema, applied_on_date, applied_to_server, was_executed, succeeded, report)
VALUES
(
'patch.db-V0-add-index-test', 'Manually executed script upgrade V0: Added new index test',
0, 15000, -1, 15001, null, 'UNKNOWN', ${TRUE}, ${TRUE}, 'Script completed'
);