/* * Copyright 2016 StreamSets Inc. * * Licensed under the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.streamsets.pipeline.lib.util; import com.google.common.base.Charsets; import com.google.common.base.Optional; import com.google.common.io.Resources; import com.streamsets.pipeline.lib.data.DataFactory; import org.apache.avro.Schema; import org.junit.Rule; import org.junit.Test; import org.mockserver.client.server.MockServerClient; import org.mockserver.junit.MockServerRule; import org.mockserver.model.Header; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import static com.streamsets.pipeline.lib.util.AvroSchemaHelper.ID_SIZE; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.mockserver.matchers.Times.exactly; import static org.mockserver.model.Header.header; import static org.mockserver.model.HttpRequest.request; import static org.mockserver.model.HttpResponse.response; import static org.mockserver.model.HttpStatusCode.OK_200; public class AvroSchemaHelperIT { private static final Header contentJson = header("Content-Type", "application/vnd.schemaregistry.v1+json"); private static final String localhost = "127.0.0.1"; @Rule public MockServerRule mockServerRule = new MockServerRule(this); private final String address = "http://" + localhost + ":" + mockServerRule.getPort(); @Test public void hasRegistryClient() throws Exception { assertFalse(new AvroSchemaHelper(getSettings(null)).hasRegistryClient()); assertTrue(new AvroSchemaHelper(getSettings(address)).hasRegistryClient()); } @Test public void loadFromRegistry() throws Exception { new MockServerClient(localhost, mockServerRule.getPort()) .when( request() .withMethod("GET") .withPath("/schemas/ids/1"), exactly(1) ) .respond( response() .withStatusCode(OK_200.code()) .withHeader(contentJson) .withBody(getBody("schema-registry/schema_1_resp.json")) ); AvroSchemaHelper helper = new AvroSchemaHelper(getSettings(address)); Schema schema = new Schema.Parser() .setValidate(true) .setValidateDefaults(true) .parse(getBody("schema-registry/schema_1.json")); assertEquals(schema, helper.loadFromRegistry(1)); Map<String, Object> expectedDefaultValues = new HashMap<>(); expectedDefaultValues.put("TestRecord.a", ""); expectedDefaultValues.put("TestRecord.b", 0L); expectedDefaultValues.put("TestRecord.c", false); assertEquals(expectedDefaultValues, AvroSchemaHelper.getDefaultValues(schema)); } @Test public void loadFromString() throws Exception { AvroSchemaHelper helper = new AvroSchemaHelper(getSettings(null)); final String schemaString = getBody("schema-registry/schema_1.json"); Schema schema = new Schema.Parser() .setValidate(true) .setValidateDefaults(true) .parse(schemaString); assertEquals(schema, helper.loadFromString(schemaString)); } @Test public void registerSchema() throws Exception { new MockServerClient(localhost, mockServerRule.getPort()) .when( request() .withHeader(header("Content-Type", "application/vnd.schemaregistry.v1+json")) .withMethod("POST") .withPath("/subjects/topic1-value/versions"), exactly(1) ) .respond( response() .withStatusCode(OK_200.code()) .withHeader(contentJson) .withBody("{\"id\":1}") ); AvroSchemaHelper helper = new AvroSchemaHelper(getSettings(address)); final String schemaString = getBody("schema-registry/schema_1.json"); Schema schema = new Schema.Parser() .setValidate(true) .setValidateDefaults(true) .parse(schemaString); assertEquals(1, helper.registerSchema(schema, "topic1-value")); } @Test public void loadFromRegistryBySchemaId() throws Exception { new MockServerClient(localhost, mockServerRule.getPort()) .when( request() .withHeader(header("Content-Type", "application/vnd.schemaregistry.v1+json")) .withMethod("GET") .withPath("/schemas/ids/1"), exactly(1) ) .respond( response() .withStatusCode(OK_200.code()) .withHeader(contentJson) .withBody(getBody("schema-registry/schema_1_resp.json")) ); AvroSchemaHelper helper = new AvroSchemaHelper(getSettings(address)); final String schemaString = getBody("schema-registry/schema_1.json"); Schema schema = new Schema.Parser() .setValidate(true) .setValidateDefaults(true) .parse(schemaString); assertEquals(schema, helper.loadFromRegistry(null, 1)); } @Test public void loadFromRegistryBySubject() throws Exception { new MockServerClient(localhost, mockServerRule.getPort()) .when( request() .withHeader(header("Content-Type", "application/vnd.schemaregistry.v1+json")) .withMethod("GET") .withPath("/schemas/ids/1"), exactly(1) ) .respond( response() .withStatusCode(OK_200.code()) .withHeader(contentJson) .withBody(getBody("schema-registry/schema_1_resp.json")) ); new MockServerClient(localhost, mockServerRule.getPort()) .when( request() .withHeader(header("Content-Type", "application/vnd.schemaregistry.v1+json")) .withMethod("GET") .withPath("/subjects/topic1-value/versions/latest"), exactly(1) ) .respond( response() .withStatusCode(OK_200.code()) .withHeader(contentJson) .withBody(getBody("schema-registry/schema_1_subject_resp.json")) ); AvroSchemaHelper helper = new AvroSchemaHelper(getSettings(address)); final String schemaString = getBody("schema-registry/schema_1.json"); Schema schema = new Schema.Parser() .setValidate(true) .setValidateDefaults(true) .parse(schemaString); assertEquals(schema, helper.loadFromRegistry("topic1-value", 0)); } @Test public void writeSchemaId() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); AvroSchemaHelper helper = new AvroSchemaHelper(getSettings(null)); helper.writeSchemaId(out, 5000); ByteBuffer buf = ByteBuffer.wrap(out.toByteArray()); assertEquals(AvroSchemaHelper.MAGIC_BYTE, buf.get()); assertEquals(5000, buf.getInt()); } @Test public void detectSchemaId() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); out.write(AvroSchemaHelper.MAGIC_BYTE); out.write(ByteBuffer.allocate(ID_SIZE).putInt(12345).array()); AvroSchemaHelper helper = new AvroSchemaHelper(getSettings(null)); Optional<Integer> schemaId = helper.detectSchemaId(out.toByteArray()); assertTrue(schemaId.isPresent()); assertEquals(12345, (int) schemaId.get()); } @Test public void detectSchemaIdNoMagicByte() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); out.write(0x01); out.write(ByteBuffer.allocate(ID_SIZE).putInt(12345).array()); AvroSchemaHelper helper = new AvroSchemaHelper(getSettings(null)); Optional<Integer> schemaId = helper.detectSchemaId(out.toByteArray()); assertFalse(schemaId.isPresent()); } @Test public void detectInvalidSchemaId() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); out.write(AvroSchemaHelper.MAGIC_BYTE); AvroSchemaHelper helper = new AvroSchemaHelper(getSettings(null)); Optional<Integer> schemaId = helper.detectSchemaId(out.toByteArray()); assertEquals(Optional.absent(), schemaId); } private String getBody(String path) throws IOException { return Resources.toString(Resources.getResource(path), Charsets.UTF_8); } private DataFactory.Settings getSettings(String repoUrl) { List<String> urls = new ArrayList<>(); if (repoUrl != null) { urls.add(repoUrl); } DataFactory.Settings settings = mock(DataFactory.Settings.class); when(settings.getConfig(AvroSchemaHelper.SCHEMA_REPO_URLS_KEY)).thenReturn(urls); return settings; } }