/* * Copyright (c) 2002-2017 "Neo Technology," * Network Engine for Objects in Lund AB [http://neotechnology.com] * * This file is part of Neo4j. * * Licensed 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 org.neo4j.driver.internal.messaging; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.channels.Channels; import java.nio.channels.WritableByteChannel; import java.util.ArrayList; import java.util.Collections; import org.neo4j.driver.internal.InternalNode; import org.neo4j.driver.internal.InternalPath; import org.neo4j.driver.internal.InternalRelationship; import org.neo4j.driver.internal.net.ChunkedOutput; import org.neo4j.driver.internal.packstream.PackStream; import org.neo4j.driver.internal.util.BytePrinter; import org.neo4j.driver.v1.Value; import org.neo4j.driver.v1.util.DumpMessage; import static java.util.Arrays.asList; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.startsWith; import static org.neo4j.driver.v1.Values.EmptyMap; import static org.neo4j.driver.v1.Values.ofValue; import static org.neo4j.driver.v1.Values.parameters; import static org.neo4j.driver.v1.Values.value; public class MessageFormatTest { public MessageFormat format = new PackStreamMessageFormatV1(); @Rule public ExpectedException exception = ExpectedException.none(); @Test public void shouldPackAllRequests() throws Throwable { assertSerializes( new RunMessage( "Hello", parameters().asMap( ofValue())) ); assertSerializes( new RunMessage( "Hello", parameters( "a", 12 ).asMap( ofValue()) ) ); assertSerializes( new PullAllMessage() ); assertSerializes( new DiscardAllMessage() ); assertSerializes( new IgnoredMessage() ); assertSerializes( new FailureMessage( "Neo.Banana.Bork.Birk", "Hello, world!" ) ); assertSerializes( new ResetMessage() ); assertSerializes( new InitMessage( "JavaDriver/1.0.0", parameters().asMap( ofValue()) ) ); } @Test public void shouldUnpackAllResponses() throws Throwable { assertSerializes( new RecordMessage( new Value[]{value( 1337L )} ) ); //assertSerializes( new SuccessMessage( new HashMap<String,Value>() ) ); } @Test public void shouldUnpackAllValues() throws Throwable { assertSerializesValue( value( parameters( "cat", null, "dog", null ) ) ); assertSerializesValue( value( parameters( "k", 12, "a", "banana" ) ) ); assertSerializesValue( value( asList( "k", 12, "a", "banana" ) ) ); assertSerializesValue( value( new InternalNode( 1, Collections.singletonList( "User" ), parameters( "name", "Bob", "age", 45 ).asMap( ofValue()) ) ) ); assertSerializesValue( value( new InternalNode( 1 ) ) ); assertSerializesValue( value( new InternalRelationship( 1, 1, 1, "KNOWS", parameters( "name", "Bob", "age", 45 ).asMap( ofValue()) ) ) ); assertSerializesValue( value( new InternalPath( new InternalNode( 1 ), new InternalRelationship( 2, 1, 3, "KNOWS", EmptyMap.asMap( ofValue()) ), new InternalNode( 3 ), new InternalRelationship( 4, 3, 5, "LIKES", EmptyMap.asMap( ofValue()) ), new InternalNode( 5 ) ) ) ); assertSerializesValue( value( new InternalPath( new InternalNode( 1 ) ) ) ); } @Test public void shouldGiveHelpfulErrorOnMalformedNodeStruct() throws Throwable { // Given ByteArrayOutputStream out = new ByteArrayOutputStream( 128 ); WritableByteChannel writable = Channels.newChannel( out ); PackStream.Packer packer = new PackStream.Packer( new ChunkedOutput( writable ) ); packer.packStructHeader( 1, PackStreamMessageFormatV1.MSG_RECORD ); packer.packListHeader( 1 ); packer.packStructHeader( 0, PackStreamMessageFormatV1.NODE ); packer.flush(); // Expect exception.expect( RuntimeException.class ); exception.expectMessage( startsWith( "Failed to unpack value: Invalid message received, serialized NODE structures should have 3 fields, " + "received NODE structure has 0 fields." ) ); // When unpack( format, out.toByteArray() ); } private void assertSerializesValue( Value value ) throws IOException { assertSerializes( new RecordMessage( new Value[]{value} ) ); } private void assertSerializes( Message... messages ) throws IOException { // Pack final ByteArrayOutputStream out = new ByteArrayOutputStream( 128 ); MessageFormat.Writer writer = format.newWriter( Channels.newChannel( out ) ); for ( Message message : messages ) { writer.write( message ); } writer.flush(); // Unpack ArrayList<Message> unpackedMessages = unpack( format, out.toByteArray() ); assertThat( unpackedMessages.toString(), equalTo( asList( messages ).toString() ) ); } private ArrayList<Message> unpack( MessageFormat format, byte[] bytes ) throws IOException { try { ByteArrayInputStream input = new ByteArrayInputStream( bytes ); MessageFormat.Reader reader = format.newReader( Channels.newChannel( input ) ); ArrayList<Message> messages = new ArrayList<>(); DumpMessage.unpack( messages, reader ); return messages; } catch( Exception e ) { throw new RuntimeException( String.format( "Failed to unpack value: %s Raw data:\n%s", e.getMessage(), BytePrinter.hex( bytes ) ), e ); } } }