Refactor tests (#1300)
This commit is contained in:
parent
d55ecae2b2
commit
408d2fad10
23 changed files with 288 additions and 365 deletions
39
.github/workflows/test-external-links.yml
vendored
Normal file
39
.github/workflows/test-external-links.yml
vendored
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
# This workflow will build a Java project with Maven
|
||||||
|
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
|
||||||
|
|
||||||
|
name: Test External Links
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ main ]
|
||||||
|
schedule:
|
||||||
|
- cron: '0 5 * * *'
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
name: Keycloak documentation
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up JDK 1.8
|
||||||
|
uses: actions/setup-java@v1
|
||||||
|
with:
|
||||||
|
java-version: 1.8
|
||||||
|
- name: Build
|
||||||
|
run: mvn install -B -DskipTests
|
||||||
|
- name: Test
|
||||||
|
run: mvn test -B -pl tests -Dtest=ExternalLinksTest
|
||||||
|
test-product:
|
||||||
|
name: Product documentation
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up JDK 1.8
|
||||||
|
uses: actions/setup-java@v1
|
||||||
|
with:
|
||||||
|
java-version: 1.8
|
||||||
|
- name: Build
|
||||||
|
run: mvn install -B -Dproduct
|
||||||
|
- name: Test
|
||||||
|
run: mvn test -B -Dproduct -pl tests -Dtest=ExternalLinksTest
|
|
@ -1,7 +1,7 @@
|
||||||
# This workflow will build a Java project with Maven
|
# This workflow will build a Java project with Maven
|
||||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
|
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
|
||||||
|
|
||||||
name: Test
|
name: Test Guides
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
@ -11,7 +11,7 @@ on:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build and test Keycloak documentation
|
name: Keycloak documentation
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
@ -19,10 +19,12 @@ jobs:
|
||||||
uses: actions/setup-java@v1
|
uses: actions/setup-java@v1
|
||||||
with:
|
with:
|
||||||
java-version: 1.8
|
java-version: 1.8
|
||||||
- name: Build with Maven
|
- name: Build
|
||||||
run: mvn package -B
|
run: mvn install -B -DskipTests
|
||||||
|
- name: Test
|
||||||
|
run: mvn test -B -pl tests -Dtest=!ExternalLinksTest
|
||||||
build-product:
|
build-product:
|
||||||
name: Build and test Product documentation
|
name: Product documentation
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
@ -30,5 +32,7 @@ jobs:
|
||||||
uses: actions/setup-java@v1
|
uses: actions/setup-java@v1
|
||||||
with:
|
with:
|
||||||
java-version: 1.8
|
java-version: 1.8
|
||||||
- name: Build with Maven
|
- name: Build
|
||||||
run: mvn package -B -Dproduct
|
run: mvn install -B -Dproduct
|
||||||
|
- name: Test
|
||||||
|
run: mvn test -B -Dproduct -pl tests -Dtest=!ExternalLinksTest
|
6
pom.xml
6
pom.xml
|
@ -21,6 +21,7 @@
|
||||||
<version.compiler.plugin>3.6.1</version.compiler.plugin>
|
<version.compiler.plugin>3.6.1</version.compiler.plugin>
|
||||||
<version.jar.plugin>3.0.2</version.jar.plugin>
|
<version.jar.plugin>3.0.2</version.jar.plugin>
|
||||||
<version.install.plugin>2.5.2</version.install.plugin>
|
<version.install.plugin>2.5.2</version.install.plugin>
|
||||||
|
<version.surefire.plugin>2.22.2</version.surefire.plugin>
|
||||||
|
|
||||||
<maven.compiler.target>1.8</maven.compiler.target>
|
<maven.compiler.target>1.8</maven.compiler.target>
|
||||||
<maven.compiler.source>1.8</maven.compiler.source>
|
<maven.compiler.source>1.8</maven.compiler.source>
|
||||||
|
@ -129,6 +130,11 @@
|
||||||
<artifactId>maven-dependency-plugin</artifactId>
|
<artifactId>maven-dependency-plugin</artifactId>
|
||||||
<version>${version.plugin.dependency}</version>
|
<version>${version.plugin.dependency}</version>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<version>${version.surefire.plugin}</version>
|
||||||
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.keycloak.documentation</groupId>
|
<groupId>org.keycloak.documentation</groupId>
|
||||||
<artifactId>header-maven-plugin</artifactId>
|
<artifactId>header-maven-plugin</artifactId>
|
||||||
|
|
|
@ -83,9 +83,9 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>junit</artifactId>
|
<artifactId>junit-jupiter</artifactId>
|
||||||
<version>4.13.1</version>
|
<version>5.8.1</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
|
@ -1,124 +0,0 @@
|
||||||
package org.keycloak.documentation.test;
|
|
||||||
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.junit.AfterClass;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.BeforeClass;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.keycloak.documentation.test.utils.DocUtils;
|
|
||||||
import org.keycloak.documentation.test.utils.LinkUtils;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public abstract class AbstractDocsTest {
|
|
||||||
|
|
||||||
private static final Logger log = LogManager.getLogger(AbstractDocsTest.class);
|
|
||||||
|
|
||||||
protected static final Config config = new Config();
|
|
||||||
|
|
||||||
protected static LinkUtils linkUtils;
|
|
||||||
protected static String body;
|
|
||||||
protected static Boolean verbose = System.getProperties().containsKey("verbose") ? true : false;
|
|
||||||
|
|
||||||
protected DocUtils utils = new DocUtils();
|
|
||||||
|
|
||||||
protected File guideDir;
|
|
||||||
protected String guideUrl;
|
|
||||||
|
|
||||||
@BeforeClass
|
|
||||||
public static void beforeClass() {
|
|
||||||
linkUtils = new LinkUtils(config, verbose);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void before() throws IOException {
|
|
||||||
guideDir = config.getGuideDir(getGuideDirName());
|
|
||||||
guideUrl = config.getGuideBaseUrl() + "/" + config.getGuideUrlFragment(getGuideDirName()) + "/";
|
|
||||||
|
|
||||||
if (body == null) {
|
|
||||||
if (config.isLoadFromFiles()) {
|
|
||||||
File htmlFile = config.getGuideHtmlFile(getGuideDirName());
|
|
||||||
body = utils.readBody(htmlFile);
|
|
||||||
} else {
|
|
||||||
log.info("Loading guide from '" + guideUrl);
|
|
||||||
body = utils.readBody(new URL(guideUrl));
|
|
||||||
}
|
|
||||||
|
|
||||||
body = rewriteLinksToGuides(body);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterClass
|
|
||||||
public static void afterClass() {
|
|
||||||
linkUtils.close();
|
|
||||||
body = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract String getGuideDirName();
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void checkVariables() {
|
|
||||||
Set<String> missingVariables = utils.findMissingVariables(body, config.getIgnoredVariables());
|
|
||||||
checkFailures("Variables not found ", missingVariables);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void checkIncludes() {
|
|
||||||
Set<String> missingIncludes = utils.findMissingIncludes(body);
|
|
||||||
checkFailures("Includes not found", missingIncludes);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void checkImages() {
|
|
||||||
Set<String> failures = linkUtils.findInvalidImages(body, guideDir, guideUrl);
|
|
||||||
checkFailures("Images not found", failures);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void checkInternalAnchors() {
|
|
||||||
Set<String> invalidInternalAnchors = linkUtils.findInvalidInternalAnchors(body);
|
|
||||||
checkFailures("Internal anchors not found", invalidInternalAnchors);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void checkExternalLinks() throws IOException {
|
|
||||||
List<LinkUtils.InvalidLink> invalidLinks = linkUtils.findInvalidLinks(body, config.getIgnoredLinks(), config.getIgnoredLinkRedirects());
|
|
||||||
if (!invalidLinks.isEmpty()) {
|
|
||||||
Set<String> failures = new HashSet<>();
|
|
||||||
for (LinkUtils.InvalidLink l : invalidLinks) {
|
|
||||||
failures.add(l.getLink() + " (" + l.getError() + ")");
|
|
||||||
}
|
|
||||||
throw new Failures("Invalid links", failures);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkFailures(String message, Set<String> failures) {
|
|
||||||
if (!failures.isEmpty()) {
|
|
||||||
throw new Failures(message, failures);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String rewriteLinksToGuides(String body) throws MalformedURLException {
|
|
||||||
if (config.isLoadFromFiles()) {
|
|
||||||
for (Map.Entry<String, String> e : config.getGuideFragmentToDir().entrySet()) {
|
|
||||||
String originalUrl = config.getDocBaseUrl() + "/" + e.getKey() + "/";
|
|
||||||
String replacementUrl = config.getGuideHtmlFile(e.getValue()).toURI().toURL().toString();
|
|
||||||
|
|
||||||
body = body.replace("href=\"" + originalUrl, "href=\"" + replacementUrl);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
body = body.replace("href=\"" + config.getDocBaseUrl(), "href=\"" + config.getGuideBaseUrl());
|
|
||||||
}
|
|
||||||
return body;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
package org.keycloak.documentation.test;
|
|
||||||
|
|
||||||
public class AuthorizationServicesTest extends AbstractDocsTest {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getGuideDirName() {
|
|
||||||
return "authorization_services";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -18,6 +18,8 @@ public class Config {
|
||||||
|
|
||||||
private static final Logger log = LogManager.getLogger(Config.class);
|
private static final Logger log = LogManager.getLogger(Config.class);
|
||||||
|
|
||||||
|
protected static final Config instance = new Config();
|
||||||
|
|
||||||
private File docsRootDir;
|
private File docsRootDir;
|
||||||
|
|
||||||
private List<String> ignoredLinkRedirects;
|
private List<String> ignoredLinkRedirects;
|
||||||
|
@ -34,7 +36,11 @@ public class Config {
|
||||||
private Map<String, String> guideDirToFragment;
|
private Map<String, String> guideDirToFragment;
|
||||||
private Map<String, String> guideFragmentToDir;
|
private Map<String, String> guideFragmentToDir;
|
||||||
|
|
||||||
public Config() {
|
public static Config getInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Config() {
|
||||||
docsRootDir = findDocsRoot();
|
docsRootDir = findDocsRoot();
|
||||||
ignoredLinkRedirects = loadConfig("/ignored-link-redirects");
|
ignoredLinkRedirects = loadConfig("/ignored-link-redirects");
|
||||||
ignoredVariables = loadConfig("/ignored-variables");
|
ignoredVariables = loadConfig("/ignored-variables");
|
||||||
|
@ -171,7 +177,7 @@ public class Config {
|
||||||
|
|
||||||
private List<String> loadConfig(String resource) {
|
private List<String> loadConfig(String resource) {
|
||||||
try {
|
try {
|
||||||
return IOUtils.readLines(AbstractDocsTest.class.getResourceAsStream(resource), "utf-8");
|
return IOUtils.readLines(Config.class.getResourceAsStream(resource), "utf-8");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
@ -179,7 +185,7 @@ public class Config {
|
||||||
|
|
||||||
private Map<String, String> loadConfigMap(String resource) {
|
private Map<String, String> loadConfigMap(String resource) {
|
||||||
try {
|
try {
|
||||||
List<String> lines = IOUtils.readLines(AbstractDocsTest.class.getResourceAsStream(resource), "utf-8");
|
List<String> lines = IOUtils.readLines(Config.class.getResourceAsStream(resource), "utf-8");
|
||||||
Map<String, String> m = new HashMap<>();
|
Map<String, String> m = new HashMap<>();
|
||||||
for (String l : lines) {
|
for (String l : lines) {
|
||||||
String[] s = l.split("=");
|
String[] s = l.split("=");
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
package org.keycloak.documentation.test;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
import org.keycloak.documentation.test.utils.LinkUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ExternalLinksTest {
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("org.keycloak.documentation.test.Guides#guides()")
|
||||||
|
public void checkExternalLinks(String guideName) throws IOException {
|
||||||
|
Guide guide = new Guide(guideName);
|
||||||
|
List<LinkUtils.InvalidLink> invalidLinks = LinkUtils.getInstance().findInvalidLinks(guide);
|
||||||
|
if (!invalidLinks.isEmpty()) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("Broken links (" + invalidLinks.size() + "):");
|
||||||
|
|
||||||
|
for (LinkUtils.InvalidLink l : invalidLinks) {
|
||||||
|
sb.append("\n\t\t- " + l.getLink() + " (" + l.getError() + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
Assertions.fail(sb.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,35 +0,0 @@
|
||||||
package org.keycloak.documentation.test;
|
|
||||||
|
|
||||||
import java.io.PrintStream;
|
|
||||||
import java.io.PrintWriter;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class Failures extends AssertionError {
|
|
||||||
|
|
||||||
private Set<String> failures;
|
|
||||||
|
|
||||||
public Failures(String error, Set<String> failures) {
|
|
||||||
super(error);
|
|
||||||
this.failures = failures;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void printStackTrace() {
|
|
||||||
printStackTrace(System.out);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void printStackTrace(PrintStream s) {
|
|
||||||
for (String f : failures) {
|
|
||||||
s.println("* " + f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void printStackTrace(PrintWriter s) {
|
|
||||||
for (String f : failures) {
|
|
||||||
s.println("* " + f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
package org.keycloak.documentation.test;
|
|
||||||
|
|
||||||
import org.junit.Assume;
|
|
||||||
import org.junit.BeforeClass;
|
|
||||||
|
|
||||||
public class GettingStartedTest extends AbstractDocsTest {
|
|
||||||
|
|
||||||
@BeforeClass
|
|
||||||
public static void skipOnCommunity() {
|
|
||||||
Assume.assumeTrue("Skipping product OpenShift guide testing in community", System.getProperties().containsKey("product"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getGuideDirName() {
|
|
||||||
return "getting_started";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
package org.keycloak.documentation.test;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.keycloak.documentation.test.utils.DocUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class Guide {
|
||||||
|
|
||||||
|
private static final Logger log = LogManager.getLogger(Guide.class);
|
||||||
|
|
||||||
|
protected DocUtils utils = new DocUtils();
|
||||||
|
|
||||||
|
private String guide;
|
||||||
|
|
||||||
|
private String body;
|
||||||
|
|
||||||
|
private File guideDir;
|
||||||
|
private String guideUrl;
|
||||||
|
|
||||||
|
public Guide(String guide) throws IOException {
|
||||||
|
this.guide = guide;
|
||||||
|
|
||||||
|
Config config = Config.getInstance();
|
||||||
|
|
||||||
|
guideDir = config.getGuideDir(guide);
|
||||||
|
guideUrl = config.getGuideBaseUrl() + "/" + config.getGuideUrlFragment(guide) + "/";
|
||||||
|
|
||||||
|
if (body == null) {
|
||||||
|
if (config.isLoadFromFiles()) {
|
||||||
|
File htmlFile = config.getGuideHtmlFile(guide);
|
||||||
|
body = utils.readBody(htmlFile);
|
||||||
|
} else {
|
||||||
|
log.info("Loading guide from '" + guideUrl);
|
||||||
|
body = utils.readBody(new URL(guideUrl));
|
||||||
|
}
|
||||||
|
|
||||||
|
body = rewriteLinksToGuides(config, body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBody() {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getDir() {
|
||||||
|
return guideDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return guideUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String rewriteLinksToGuides(Config config, String body) throws MalformedURLException {
|
||||||
|
if (config.isLoadFromFiles()) {
|
||||||
|
for (Map.Entry<String, String> e : config.getGuideFragmentToDir().entrySet()) {
|
||||||
|
String originalUrl = config.getDocBaseUrl() + "/" + e.getKey() + "/";
|
||||||
|
String replacementUrl = config.getGuideHtmlFile(e.getValue()).toURI().toURL().toString();
|
||||||
|
|
||||||
|
body = body.replace("href=\"" + originalUrl, "href=\"" + replacementUrl);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
body = body.replace("href=\"" + config.getDocBaseUrl(), "href=\"" + config.getGuideBaseUrl());
|
||||||
|
}
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
package org.keycloak.documentation.test;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
import org.keycloak.documentation.test.utils.DocUtils;
|
||||||
|
import org.keycloak.documentation.test.utils.LinkUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class GuideTest {
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("org.keycloak.documentation.test.Guides#guides()")
|
||||||
|
public void checkVariables(String guideName) throws IOException {
|
||||||
|
Set<String> missingVariables = DocUtils.findMissingVariables(new Guide(guideName));
|
||||||
|
checkFailures("Variables not found ", missingVariables);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("org.keycloak.documentation.test.Guides#guides()")
|
||||||
|
public void checkIncludes(String guideName) throws IOException {
|
||||||
|
Set<String> missingIncludes = DocUtils.findMissingIncludes(new Guide(guideName));
|
||||||
|
checkFailures("Includes not found", missingIncludes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("org.keycloak.documentation.test.Guides#guides()")
|
||||||
|
public void checkImages(String guideName) throws IOException {
|
||||||
|
Set<String> failures = LinkUtils.getInstance().findInvalidImages(new Guide(guideName));
|
||||||
|
checkFailures("Images not found", failures);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("org.keycloak.documentation.test.Guides#guides()")
|
||||||
|
public void checkInternalAnchors(String guideName) throws IOException {
|
||||||
|
Set<String> invalidInternalAnchors = LinkUtils.getInstance().findInvalidInternalAnchors(new Guide(guideName));
|
||||||
|
checkFailures("Internal anchors not found", invalidInternalAnchors);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkFailures(String message, Set<String> failures) {
|
||||||
|
if (!failures.isEmpty()) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(message + " (" + failures.size() + "):");
|
||||||
|
|
||||||
|
for (String f : failures) {
|
||||||
|
sb.append("\n\t\t- " + f);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assertions.fail(sb.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package org.keycloak.documentation.test;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Guides {
|
||||||
|
|
||||||
|
private static String[] guides;
|
||||||
|
static {
|
||||||
|
boolean product = System.getProperties().containsKey("product");
|
||||||
|
|
||||||
|
List<String> g = new LinkedList<>();
|
||||||
|
g.add("authorization_services");
|
||||||
|
g.add("release_notes");
|
||||||
|
g.add("securing_apps");
|
||||||
|
g.add("server_admin");
|
||||||
|
g.add("server_development");
|
||||||
|
g.add("server_installation");
|
||||||
|
g.add("upgrading");
|
||||||
|
|
||||||
|
if (product) {
|
||||||
|
g.add("getting_started");
|
||||||
|
g.add("openshift");
|
||||||
|
}
|
||||||
|
|
||||||
|
guides = g.toArray(new String[g.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String[] guides() {
|
||||||
|
return guides;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,18 +0,0 @@
|
||||||
package org.keycloak.documentation.test;
|
|
||||||
|
|
||||||
import org.junit.Assume;
|
|
||||||
import org.junit.BeforeClass;
|
|
||||||
|
|
||||||
public class OpenShiftTest extends AbstractDocsTest {
|
|
||||||
|
|
||||||
@BeforeClass
|
|
||||||
public static void skipOnCommunity() {
|
|
||||||
Assume.assumeTrue("Skipping product OpenShift guide testing in community", System.getProperties().containsKey("product"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getGuideDirName() {
|
|
||||||
return "openshift";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
package org.keycloak.documentation.test;
|
|
||||||
|
|
||||||
public class ReleaseNotesTest extends AbstractDocsTest {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getGuideDirName() {
|
|
||||||
return "release_notes";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
package org.keycloak.documentation.test;
|
|
||||||
|
|
||||||
public class SecuringAppsTest extends AbstractDocsTest {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getGuideDirName() {
|
|
||||||
return "securing_apps";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
package org.keycloak.documentation.test;
|
|
||||||
|
|
||||||
public class ServerAdminTest extends AbstractDocsTest {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getGuideDirName() {
|
|
||||||
return "server_admin";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
package org.keycloak.documentation.test;
|
|
||||||
|
|
||||||
public class ServerDeveloperTest extends AbstractDocsTest {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getGuideDirName() {
|
|
||||||
return "server_development";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
package org.keycloak.documentation.test;
|
|
||||||
|
|
||||||
public class ServerInstallationTest extends AbstractDocsTest {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getGuideDirName() {
|
|
||||||
return "server_installation";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
package org.keycloak.documentation.test;
|
|
||||||
|
|
||||||
public class UpgradingTest extends AbstractDocsTest {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getGuideDirName() {
|
|
||||||
return "upgrading";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -2,6 +2,8 @@ package org.keycloak.documentation.test.utils;
|
||||||
|
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.keycloak.documentation.test.Config;
|
||||||
|
import org.keycloak.documentation.test.Guide;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -16,7 +18,7 @@ import java.util.regex.Pattern;
|
||||||
|
|
||||||
public class DocUtils {
|
public class DocUtils {
|
||||||
|
|
||||||
public String readBody(File htmlFile) throws IOException {
|
public static String readBody(File htmlFile) throws IOException {
|
||||||
String s = FileUtils.readFileToString(htmlFile, "utf-8");
|
String s = FileUtils.readFileToString(htmlFile, "utf-8");
|
||||||
|
|
||||||
Pattern p = Pattern.compile("<body.*?>(.*?)</body>.*?",Pattern.DOTALL);
|
Pattern p = Pattern.compile("<body.*?>(.*?)</body>.*?",Pattern.DOTALL);
|
||||||
|
@ -26,7 +28,7 @@ public class DocUtils {
|
||||||
return m.group(1);
|
return m.group(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String readBody(URL url) throws IOException {
|
public static String readBody(URL url) throws IOException {
|
||||||
HttpURLConnection connection = null;
|
HttpURLConnection connection = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -60,10 +62,11 @@ public class DocUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> findMissingVariables(String body, List<String> ignoredVariables) {
|
public static Set<String> findMissingVariables(Guide guide) {
|
||||||
|
List<String> ignoredVariables = Config.getInstance().getIgnoredVariables();
|
||||||
Set<String> missingVariables = new HashSet<>();
|
Set<String> missingVariables = new HashSet<>();
|
||||||
Pattern p = Pattern.compile("[^$/=\n]\\{([^ }\"]*)}");
|
Pattern p = Pattern.compile("[^$/=\n]\\{([^ }\"]*)}");
|
||||||
Matcher m = p.matcher(body);
|
Matcher m = p.matcher(guide.getBody());
|
||||||
while (m.find()) {
|
while (m.find()) {
|
||||||
String key = m.group(1);
|
String key = m.group(1);
|
||||||
if (!key.isEmpty() && !ignoredVariables.contains(key)) {
|
if (!key.isEmpty() && !ignoredVariables.contains(key)) {
|
||||||
|
@ -73,10 +76,10 @@ public class DocUtils {
|
||||||
return missingVariables;
|
return missingVariables;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> findMissingIncludes(String body) {
|
public static Set<String> findMissingIncludes(Guide guide) {
|
||||||
Set<String> missingIncludes = new HashSet<>();
|
Set<String> missingIncludes = new HashSet<>();
|
||||||
Pattern p = Pattern.compile("Unresolved directive.*");
|
Pattern p = Pattern.compile("Unresolved directive.*");
|
||||||
Matcher m = p.matcher(body);
|
Matcher m = p.matcher(guide.getBody());
|
||||||
if (m.find()) {
|
if (m.find()) {
|
||||||
missingIncludes.add(m.group());
|
missingIncludes.add(m.group());
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,6 @@ import org.apache.hc.client5.http.impl.DefaultHttpRequestRetryStrategy;
|
||||||
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
|
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
|
||||||
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
|
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
|
||||||
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
|
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
|
||||||
import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
|
|
||||||
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
|
|
||||||
import org.apache.hc.core5.http.ClassicHttpResponse;
|
import org.apache.hc.core5.http.ClassicHttpResponse;
|
||||||
import org.apache.hc.core5.http.HttpEntity;
|
import org.apache.hc.core5.http.HttpEntity;
|
||||||
import org.apache.hc.core5.http.HttpStatus;
|
import org.apache.hc.core5.http.HttpStatus;
|
||||||
|
@ -19,11 +17,6 @@ import org.apache.hc.core5.http.io.entity.EntityUtils;
|
||||||
import org.apache.hc.core5.util.TimeValue;
|
import org.apache.hc.core5.util.TimeValue;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.cert.X509Certificate;
|
|
||||||
import javax.net.ssl.SSLContext;
|
|
||||||
import javax.net.ssl.SSLException;
|
|
||||||
import javax.net.ssl.SSLSession;
|
|
||||||
import javax.net.ssl.X509TrustManager;
|
|
||||||
|
|
||||||
public class HttpUtils {
|
public class HttpUtils {
|
||||||
|
|
||||||
|
@ -86,7 +79,6 @@ public class HttpUtils {
|
||||||
try {
|
try {
|
||||||
client.execute(method, responseHandler);
|
client.execute(method, responseHandler);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
|
||||||
response.setError("exception " + e.getMessage());
|
response.setError("exception " + e.getMessage());
|
||||||
response.setSuccess(false);
|
response.setSuccess(false);
|
||||||
}
|
}
|
||||||
|
@ -104,50 +96,11 @@ public class HttpUtils {
|
||||||
.disableRedirectHandling()
|
.disableRedirectHandling()
|
||||||
.setConnectionManager(
|
.setConnectionManager(
|
||||||
PoolingHttpClientConnectionManagerBuilder.create()
|
PoolingHttpClientConnectionManagerBuilder.create()
|
||||||
.setSSLSocketFactory(new NoopSSLConnectionSocketFactory())
|
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class NoopSSLConnectionSocketFactory extends SSLConnectionSocketFactory {
|
|
||||||
private static SSLContext sslContext;
|
|
||||||
|
|
||||||
static {
|
|
||||||
try {
|
|
||||||
sslContext = SSLContext.getInstance("TLS");
|
|
||||||
sslContext.init(
|
|
||||||
null,
|
|
||||||
new X509TrustManager[] {
|
|
||||||
new X509TrustManager() {
|
|
||||||
public X509Certificate[] getAcceptedIssuers() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void checkClientTrusted(X509Certificate[] certs, String authType) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void checkServerTrusted(X509Certificate[] certs, String authType) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
null
|
|
||||||
);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public NoopSSLConnectionSocketFactory() {
|
|
||||||
super(sslContext, new NoopHostnameVerifier());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void verifySession(String hostname, SSLSession sslSession) throws SSLException {
|
|
||||||
// no-op
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Response {
|
public static class Response {
|
||||||
|
|
||||||
private boolean success;
|
private boolean success;
|
||||||
|
|
|
@ -4,6 +4,7 @@ import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.keycloak.documentation.test.Config;
|
import org.keycloak.documentation.test.Config;
|
||||||
|
import org.keycloak.documentation.test.Guide;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
|
@ -18,16 +19,18 @@ public class LinkUtils {
|
||||||
|
|
||||||
private static final Logger logger = LogManager.getLogger(LinkUtils.class);
|
private static final Logger logger = LogManager.getLogger(LinkUtils.class);
|
||||||
|
|
||||||
|
private static final LinkUtils instance = new LinkUtils();
|
||||||
|
|
||||||
private HttpUtils http = new HttpUtils();
|
private HttpUtils http = new HttpUtils();
|
||||||
private Config config;
|
|
||||||
private File verifiedLinksCacheFile;
|
private File verifiedLinksCacheFile;
|
||||||
private boolean verbose;
|
|
||||||
private Map<String, Long> verifiedLinks;
|
private Map<String, Long> verifiedLinks;
|
||||||
|
|
||||||
public LinkUtils(Config config, boolean verbose) {
|
public static LinkUtils getInstance() {
|
||||||
this.config = config;
|
return instance;
|
||||||
this.verifiedLinksCacheFile = config.getVerifiedLinksCache();
|
}
|
||||||
this.verbose = verbose;
|
|
||||||
|
private LinkUtils() {
|
||||||
|
this.verifiedLinksCacheFile = Config.getInstance().getVerifiedLinksCache();
|
||||||
this.verifiedLinks = loadCheckedLinksCache();
|
this.verifiedLinks = loadCheckedLinksCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,15 +38,15 @@ public class LinkUtils {
|
||||||
saveCheckedLinksCache();
|
saveCheckedLinksCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> findInvalidInternalAnchors(String body) {
|
public Set<String> findInvalidInternalAnchors(Guide guide) {
|
||||||
Set<String> invalidInternalAnchors = new HashSet<>();
|
Set<String> invalidInternalAnchors = new HashSet<>();
|
||||||
Pattern p = Pattern.compile("<a href=\"([^ \"]*)[^>]*\">");
|
Pattern p = Pattern.compile("<a href=\"([^ \"]*)[^>]*\">");
|
||||||
Matcher m = p.matcher(body);
|
Matcher m = p.matcher(guide.getBody());
|
||||||
while (m.find()) {
|
while (m.find()) {
|
||||||
String link = m.group(1);
|
String link = m.group(1);
|
||||||
|
|
||||||
if (link.startsWith("#")) {
|
if (link.startsWith("#")) {
|
||||||
if (!body.contains("id=\"" + link.substring(1) + "\"")) {
|
if (!guide.getBody().contains("id=\"" + link.substring(1) + "\"")) {
|
||||||
invalidInternalAnchors.add(link.substring(1));
|
invalidInternalAnchors.add(link.substring(1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,14 +54,14 @@ public class LinkUtils {
|
||||||
return invalidInternalAnchors;
|
return invalidInternalAnchors;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<InvalidLink> findInvalidLinks(String body, List<String> ignoredLinks, List<String> ignoredLinkRedirects) throws IOException {
|
public List<InvalidLink> findInvalidLinks(Guide guide) throws IOException {
|
||||||
List<InvalidLink> invalidLinks = new LinkedList<>();
|
List<InvalidLink> invalidLinks = new LinkedList<>();
|
||||||
Pattern p = Pattern.compile("<a href=\"([^ \"]*)[^>]*\">");
|
Pattern p = Pattern.compile("<a href=\"([^ \"]*)[^>]*\">");
|
||||||
Matcher m = p.matcher(body);
|
Matcher m = p.matcher(guide.getBody());
|
||||||
while (m.find()) {
|
while (m.find()) {
|
||||||
String link = m.group(1);
|
String link = m.group(1);
|
||||||
|
|
||||||
if (verifyLink(link, ignoredLinks, invalidLinks)) {
|
if (verifyLink(link, Config.getInstance().getIgnoredLinks(), invalidLinks)) {
|
||||||
if (link.startsWith("http")) {
|
if (link.startsWith("http")) {
|
||||||
String anchor = link.contains("#") ? link.split("#")[1] : null;
|
String anchor = link.contains("#") ? link.split("#")[1] : null;
|
||||||
String error = null;
|
String error = null;
|
||||||
|
@ -66,7 +69,7 @@ public class LinkUtils {
|
||||||
HttpUtils.Response response = anchor != null ? http.load(link) : http.isValid(link);
|
HttpUtils.Response response = anchor != null ? http.load(link) : http.isValid(link);
|
||||||
|
|
||||||
if (response.getRedirectLocation() != null) {
|
if (response.getRedirectLocation() != null) {
|
||||||
if (!validRedirect(response.getRedirectLocation(), ignoredLinkRedirects)) {
|
if (!validRedirect(response.getRedirectLocation(), Config.getInstance().getIgnoredLinkRedirects())) {
|
||||||
error = "invalid redirect to " + response.getRedirectLocation();
|
error = "invalid redirect to " + response.getRedirectLocation();
|
||||||
}
|
}
|
||||||
} else if (response.isSuccess() && anchor != null) {
|
} else if (response.isSuccess() && anchor != null) {
|
||||||
|
@ -79,16 +82,8 @@ public class LinkUtils {
|
||||||
|
|
||||||
if (error == null) {
|
if (error == null) {
|
||||||
verifiedLinks.put(link, System.currentTimeMillis());
|
verifiedLinks.put(link, System.currentTimeMillis());
|
||||||
|
|
||||||
if (verbose) {
|
|
||||||
System.out.println("[OK] " + link);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
invalidLinks.add(new InvalidLink(link, error));
|
invalidLinks.add(new InvalidLink(link, error));
|
||||||
|
|
||||||
if (verbose) {
|
|
||||||
System.out.println("[BAD] " + link);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (link.startsWith("file")) {
|
} else if (link.startsWith("file")) {
|
||||||
File f = new File(new URL(link).getFile());
|
File f = new File(new URL(link).getFile());
|
||||||
|
@ -110,36 +105,28 @@ public class LinkUtils {
|
||||||
return invalidLinks;
|
return invalidLinks;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> findInvalidImages(String body, File guideDir, String guideUrl) {
|
public Set<String> findInvalidImages(Guide guide) {
|
||||||
Set<String> missingImages = new HashSet<>();
|
Set<String> missingImages = new HashSet<>();
|
||||||
Pattern p = Pattern.compile("<img src=\"([^ \"]*)[^>]*\"");
|
Pattern p = Pattern.compile("<img src=\"([^ \"]*)[^>]*\"");
|
||||||
Matcher m = p.matcher(body);
|
Matcher m = p.matcher(guide.getBody());
|
||||||
while (m.find()) {
|
while (m.find()) {
|
||||||
String image = m.group(1);
|
String image = m.group(1);
|
||||||
if (config.isLoadFromFiles()) {
|
if (Config.getInstance().isLoadFromFiles()) {
|
||||||
File f = new File(guideDir, image);
|
File f = new File(guide.getDir(), image);
|
||||||
if (!f.isFile()) {
|
if (!f.isFile()) {
|
||||||
missingImages.add(image);
|
missingImages.add(image);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (image.startsWith("./")) {
|
if (image.startsWith("./")) {
|
||||||
image = guideUrl + image;
|
image = guide.getUrl() + image;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!verifiedLinks.containsKey(image)) {
|
if (!verifiedLinks.containsKey(image)) {
|
||||||
boolean valid = http.isValid(image).isSuccess();
|
boolean valid = http.isValid(image).isSuccess();
|
||||||
if (valid) {
|
if (valid) {
|
||||||
verifiedLinks.put(image, System.currentTimeMillis());
|
verifiedLinks.put(image, System.currentTimeMillis());
|
||||||
|
|
||||||
if (verbose) {
|
|
||||||
System.out.println("[OK] " + image);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
missingImages.add(image);
|
missingImages.add(image);
|
||||||
|
|
||||||
if (verbose) {
|
|
||||||
System.out.println("[BAD] " + image);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue