Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2023 Fraunhofer IWU.
* Copyright (c) 2024 Fraunhofer IWU.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -15,114 +15,129 @@
*/
package io.github.linkedfactory.core.kvin.util;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.github.linkedfactory.core.kvin.Kvin;
import io.github.linkedfactory.core.kvin.KvinTuple;
import io.github.linkedfactory.core.kvin.Record;
import net.enilink.commons.iterator.IExtendedIterator;

import net.enilink.komma.core.URIs;
import org.junit.Test;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

public class JsonFormatParserTest {
private final ObjectMapper mapper = new ObjectMapper();

@Test
public void shouldParseJsonResource() throws Exception {
List<KvinTuple> tuples = parse(readResource("/JsonFormatParserTestContent.json"));

@Test
public void shouldParseJson() throws IOException {
JsonFormatParser jsonParser = new JsonFormatParser(
getClass().getClassLoader().getResourceAsStream("JsonFormatParserTestContent.json"));
IExtendedIterator<KvinTuple> tuples = jsonParser.parse();
assertNotNull(tuples);
int index = 0;
while (tuples.hasNext()) {
KvinTuple t = tuples.next();
if (index == 2) {
assertTrue(t.value instanceof Integer);
} else if (index == 3) {
assertTrue(t.value instanceof BigInteger);
} else if (index == 4) {
assertTrue(t.value instanceof Double);
} else if (index == 5) {
assertTrue(t.value instanceof Long);
} else if (index == 6) {
assertTrue(t.value instanceof Boolean);
} else if (index == 7 || index == 10) {
assertTrue(t.value instanceof Record);
}
index++;
}
assertEquals(11, index);
}

@Test
public void shouldThrowOnMalformedJson() {
String malformedJson = "{ \"item1\": { \"prop1\": [ { \"value\": 123 } ] "; // missing closing braces
try {
JsonFormatParser parser = new JsonFormatParser(
new ByteArrayInputStream(malformedJson.getBytes(StandardCharsets.UTF_8)));
var it = parser.parse();
while (it.hasNext()) {
it.next();
}
fail("Expected RuntimeException due to malformed JSON");
} catch (Exception e) {
assertTrue(e.getCause() instanceof IOException);
}
}

@Test
public void shouldThrowOnMissingValueField() {
String missingValueJson = "{ \"item1\": { \"prop1\": [ { \"seqNr\": 1 } ] } }";
try {
JsonFormatParser parser = new JsonFormatParser(
new ByteArrayInputStream(missingValueJson.getBytes(StandardCharsets.UTF_8)));
parser.parse().hasNext();
fail("Expected RuntimeException due to missing 'value' field");
} catch (Exception e) {
assertTrue(e.getCause() instanceof IOException);
}
}

@Test
public void shouldThrowOnEmptyItemName() {
String emptyItemNameJson = "{ \"\": { \"prop1\": [ { \"value\": 1 } ] } }";
try {
JsonFormatParser parser = new JsonFormatParser(
new ByteArrayInputStream(emptyItemNameJson.getBytes(StandardCharsets.UTF_8)));
parser.parse().hasNext();
fail("Expected RuntimeException due to empty item name");
} catch (Exception e) {
assertTrue(e.getCause() instanceof IOException);
}
}

@Test
public void shouldThrowOnEmptyPropertyName() {
String emptyPropertyNameJson = "{ \"item1\": { \"\": [ { \"value\": 1 } ] } }";
try {
JsonFormatParser parser = new JsonFormatParser(
new ByteArrayInputStream(emptyPropertyNameJson.getBytes(StandardCharsets.UTF_8)));
parser.parse().hasNext();
fail("Expected RuntimeException due to empty property name");
} catch (Exception e) {
assertTrue(e.getCause() instanceof IOException);
}
}
assertEquals(11, tuples.size());
assertTrue(tuples.get(2).value instanceof java.lang.Integer);
assertTrue(tuples.get(3).value instanceof java.math.BigInteger);
assertTrue(tuples.get(4).value instanceof java.lang.Double);
assertTrue(tuples.get(5).value instanceof java.lang.Long);
assertTrue(tuples.get(6).value instanceof java.lang.Boolean);
assertTrue(tuples.get(7).value instanceof Record);
assertTrue(tuples.get(10).value instanceof Record);
}

@Test
public void testUriWithSpaces() {
String jsonWithSpacesInUri = "{ \"http://example.com/item 1\": { \"http://example.com/prop 1\": [ { \"value\": 1 } ] } }";
public void shouldResolvePrefixesFromContext() throws Exception {
String json = """
{
"@context": {"pref": "http://test1.example/", "pref2": "http://pref2.example/"},
"@context": {"pref": "http://test2.example/"},
"pref": {
"pref:rest": [{"value": "val"}],
"pref2:rest": [{"value": "val2"}]
}
}""";

List<KvinTuple> tuples = parse(json, 1619424246100L);

assertEquals(2, tuples.size());
assertEquals("http://test2.example/", tuples.getFirst().item.toString());
assertEquals("http://test2.example/rest", tuples.get(0).property.toString());
assertEquals("val", tuples.get(0).value);
assertEquals("http://test2.example/", tuples.get(1).item.toString());
assertEquals("http://pref2.example/rest", tuples.get(1).property.toString());
assertEquals("val2", tuples.get(1).value);
}

@Test
public void shouldParseNestedRecords() throws Exception {
ObjectNode root = mapper.createObjectNode();
ObjectNode item = root.putObject("http://example.root/item");
ArrayNode values = item.putArray("http://example.root/nested");
ObjectNode value = values.addObject().putObject("value");
value.put("msg", "Error 1");
value.put("nr", 1);
value.putObject("test_prop").put("msg", "test");
value.putObject("id_prop").put("@id", "http://example.org/properties/test3");

List<KvinTuple> tuples = parse(root, 1619424246100L);
Record expectedValue = new Record(URIs.createURI("msg"), "Error 1").append(new Record(URIs.createURI("nr"), 1).append(new Record(URIs.createURI("test_prop"), new Record(URIs.createURI("msg"), "test")).append(new Record(URIs.createURI("id_prop"), URIs.createURI("http://example.org/properties/test3")))));
List<KvinTuple> expected = List.of(new KvinTuple(URIs.createURI("http://example.root/item"), URIs.createURI("http://example.root/nested"), Kvin.DEFAULT_CONTEXT, 1619424246100L, 0, expectedValue));

assertEquals(expected, tuples);
}

@Test
public void shouldRejectMissingValueField() throws Exception {
String json = "{ \"item1\": { \"prop1\": [ { \"seqNr\": 1 } ] } }";

try {
JsonFormatParser parser = new JsonFormatParser(
new ByteArrayInputStream(jsonWithSpacesInUri.getBytes(StandardCharsets.UTF_8)));
parser.parse().hasNext();
fail("Expected RuntimeException due to spaces in URI");
} catch (Exception e) {
parse(json);
fail("Expected RuntimeException due to missing 'value' field");
} catch (RuntimeException e) {
assertTrue(e.getCause() instanceof IOException);
}
}
}

private List<KvinTuple> parse(JsonNode node) throws Exception {
return parse(node, System.currentTimeMillis());
}

private List<KvinTuple> parse(JsonNode node, long currentTime) throws Exception {
return parseBytes(mapper.writeValueAsBytes(node), currentTime);
}

private List<KvinTuple> parse(String json) throws Exception {
return parse(json, System.currentTimeMillis());
}

private List<KvinTuple> parse(String json, long currentTime) throws Exception {
return parseBytes(json.getBytes(StandardCharsets.UTF_8), currentTime);
}

private List<KvinTuple> parseBytes(byte[] bytes, long currentTime) throws Exception {
JsonFormatParser parser = new JsonFormatParser(new ByteArrayInputStream(bytes));
try (var tuples = parser.parse(currentTime)) {
return tuples.toList();
}
}

private JsonNode readResource(String path) throws Exception {
InputStream stream = getClass().getResourceAsStream(path);
try (stream) {
assertNotNull(stream);
return mapper.readTree(stream);
}
}
}
Loading