Formål

Formålet med dette dokument er at beskrive hvordan et udviklingsmiljø, til videreudvikling af SEAL.JAVA services, kan sættes op, samt hvordan koden bygges, deployes og testes.
Først beskrives de softwaremæssige krav, der er til miljøet, samt hvordan kode hentes og bygges. Dernæst beskrives deploymentmiljøet.

Opsætning

Siden release 2.5.0 har Seal.java krævet Java 1.8  for at bygge

Kildekode

Kildekoden til Seal.java er efter version 2.5.11 flyttet til libraries repository  på NSPOPs subversion.

https://svn.nspop.dk/svn/libraries/sosi-seal/

Her vil fremtidige udgaver af koden blive versioneret. Udgaverne for version 2.5.9, 2.5.10 og 2.5.11 er også blevet flyttet til libraries  og kan findes her:

https://svn.nspop.dk/svn/libraries/sosi-seal/tags/

Distribution

De binære pakker er tilgængelige igennem NSPOPs Nexus (pakke manager):

https://nexus.nspop.dk/nexus/service/rest/repository/browse/public/dk/sosi/seal/seal/

Her findes også SNAPSHOT udgaver, som kan bruges under udvikling af funktionalitet, der skal testes inden release.

Bygge og unittest

Koden bygges vha. Maven.

Følgende bruges til at bygge Seal.java:

mvn -f modules/pom.xml package install

Unitests afvikles under byg, men kan også kaldes selvstændigt vha.:

mvn -f modules/pom.xml test


Udvikling

Herunder vejledning til udviklingen i forskellige områder af Seal.java

parametriserede tests

Vi bruger JUnit 4 til at teste i Seal.java og vil gerne kunne afvikle parameteriserede tests mod de funktioner, hvor afviklingen er uændret, men input er variabelt. Det har primært været et ønske ifm. introduktionen af nye OCES3 certifikater, hvor snitfladerne har været uændret, men input har været henholdsvis OCES2 og OCES3 certifikater.

Følgende side har været indgangen til brug af parametriserede tests:

https://github.com/junit-team/junit4/wiki/Parameterized-tests

I dette afsnit følger en kort introduktion til den skabelon man skal følge for at lave en standard parametriseret JUnit4 test - efterfulgt af et eksempel.

Så følger en kort beskrivelse af de problemstillinger vi har haft ifm. oprettelsen af tests. Vi gennemgår de hjælpeklasser og beslutninger, som er lavet for at give et bedre overblik og en nemmere tilgang til at lave parametriseret tests - og til sidst et eksempel på brugen af hjælpeklasserne.

Introduktion til Parameterized tests

En parametriseret JUnit4 test klasse skal afvikles med org.junit.runners.Parameterized. Derfor angives annotation @RunWith(Parameterized.class) på klassen.

Der skal angives en metode, som annoteres med org.junit.runners.Parameterized.Parameters, som er de parametre der bruges i test metoderne. Metoden returnere en java.lang.Iterable bestående af et Object[], hvor hvert Object[] er det datasæt af parametre, som et test genneløb skal bruge. Testene gennemløbes for hvert entry der er i java.lang.Iterable.

Inputtet 'name' til org.junit.runners.Parameterized.Parameters annoteringen angiver navnet på parametriseret test.

Datasæt til tests angives vha. org.junit.runners.Parameterized.Parameter. Her angiver man et index som input til annoteringen, så det peger på et specifikt Object i aktuelle Object[].

Eksempel:

@RunWith(Parameterized.class)
public class SimpleVaultTest {
	@Parameter(0)
	public X509Certificate certificateToTest; //Skal være public!
	@Parameter(1)
	public String expectedCommonName; 
	@Parameter(2)
	public String expectedSubjectSerialNumber; 
	@Parameters
	public static Iterable<Object[]> data() {
		return Arrays.asList(new Object[][] {{ CredentialVaultTestUtil.getCredentialVault().getSystemCredentialPair().getCertificate(), 
					"TU GENEREL MOCES M CPR gyldig",
					"CVR:30808460-RID:42634739"
				}, { CredentialVaultTestUtil.getMoces3CredentialVault().getSystemCredentialPair().getCertificate(), 
					"Tonnis Vestergaard",
					"UI:DK-153279bd-07cb-4128-a8b1-666107fba6d5"}});
	}
	@Test
	public void testCertificateCommonName() {
		DistinguishedName dn = new DistinguishedName(certificateToTest.getSubjectX500Principal());
		assertEquals("Forventede noget helt andet her?", expectedCommonName, dn.getCommonName());
	}
	@Test
	public void testCertificateSubjectSerialNumber() {
		DistinguishedName dn = new DistinguishedName(certificateToTest.getSubjectX500Principal());
		assertEquals("Forventede noget helt andet her?", expectedSubjectSerialNumber, dn.getSubjectSerialNumber());
	}
}

Hjælpeklasser til Parameterized tests i Seal.java

I forbindelse med implemeneteringen af parametriserede tests stødte vi nogle mindre udfordringer.

Derfor er der implementeret følgende:

Derfor er findes følgende hjælpeklasser i Seal.java:

Eksempel:

@RunWith(Parameterized.class)
public class SimpleVaultTest {
	@Parameter(0)
	public String suiteName;
	//Then
	@Parameter(1)
	public Validators validators;
	//Given
	@Parameter(2)
	public X509Certificate certificateToTest;
	
	public Date validityDate;

	@Parameters(name = "{0}")
	public static Iterable<Object[]> data() {
		TestParameterBuilder builder = new TestParameterBuilder();
		
		TestParameter credentialVaultTest = builder.newTest("CredentialVault");
		//Given
		credentialVaultTest.add(CredentialVaultTestUtil.getCredentialVault().getSystemCredentialPair().getCertificate());
		//Then
		credentialVaultTest.expect("CertificateCommonName", "TU GENEREL MOCES M CPR gyldig");
		credentialVaultTest.expect("SubjectSerialNumber", "CVR:30808460-RID:42634739");
		credentialVaultTest.success("CertificateValidity");
		credentialVaultTest.success("Custom", notNorwayAssertion());

		TestParameter moces3CredentialVaultTest = builder.newTest("Moces3CredentialVault");
		//Given
		moces3CredentialVaultTest.add(CredentialVaultTestUtil.getMoces3CredentialVault().getSystemCredentialPair().getCertificate());
		//Then
		moces3CredentialVaultTest.expect("CertificateCommonName", "Tonnis Vestergaard");
		moces3CredentialVaultTest.expect("SubjectSerialNumber", "UI:DK-153279bd-07cb-4128-a8b1-666107fba6d5");
		moces3CredentialVaultTest.fail("CertificateValidity", new CertificateNotYetValidException("NotBefore: Tue Jan 26 15:27:19 CET 2021"));
		moces3CredentialVaultTest.success("Custom", notNorwayAssertion());
		
		return builder.build();
	}
	//Custom Asserter	
	private static Asserter notNorwayAssertion() {
		return (actual) -> {
			Assert.assertNotNull("Forventede et reultat", actual);
			Assert.assertTrue("Forventer instans af DistinguishedName", actual instanceof DistinguishedName);
			DistinguishedName dn = (DistinguishedName) actual;
			Assert.assertNotEquals("Forventede ikke Norge",  "N", dn.getCountry());
		};
	}
	@Before
	public void setup() {
		validityDate = Date.from(Instant.parse("2020-01-01T00:00:00.00Z"));
	}
	//When
	@Test
	public void testCertificateCommonName() {
		DistinguishedName dn = new DistinguishedName(certificateToTest.getSubjectX500Principal());		
		validators.getValidator("CertificateCommonName").assertSuccess(dn.getCommonName());
	}
	@Test
	public void testCertificateSubjectSerialNumber() {
		DistinguishedName dn = new DistinguishedName(certificateToTest.getSubjectX500Principal());
		validators.getValidator("SubjectSerialNumber").assertSuccess(dn.getSubjectSerialNumber());
	}
	@Test
	public void testCertificateValidity() throws CertificateExpiredException, CertificateNotYetValidException {
		Validation validator = validators.getValidator("CertificateValidity");
		try {
			certificateToTest.checkValidity(validityDate);
			validator.assertSuccess();
		} catch(Exception e) {
			validator.assertFailure(e);
		}
	}
	@Test
	public void testCustomFieldValidation() {
		DistinguishedName dn = new DistinguishedName(certificateToTest.getSubjectX500Principal());		
		validators.getValidator("Custom").assertSuccess(dn);
	}
}

Output af ovenstående eksempel vil være: