com.spotify.helios.common.descriptors.JobTest.java Source code

Java tutorial

Introduction

Here is the source code for com.spotify.helios.common.descriptors.JobTest.java

Source

/*
 * Copyright (c) 2014 Spotify AB.
 *
 * 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 com.spotify.helios.common.descriptors;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.io.BaseEncoding;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.spotify.helios.common.Hash;
import com.spotify.helios.common.Json;

import org.junit.Test;

import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static com.google.common.base.Charsets.UTF_8;
import static com.google.common.base.Preconditions.checkArgument;
import static com.spotify.helios.common.descriptors.Descriptor.parse;
import static java.util.Arrays.asList;
import static org.hamcrest.Matchers.hasEntry;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;

public class JobTest {

    private Map<String, Object> map(final Object... objects) {
        final ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
        checkArgument(objects.length % 2 == 0);
        for (int i = 0; i < objects.length; i += 2) {
            builder.put((String) objects[i], objects[i + 1]);
        }
        return builder.build();
    }

    @Test
    public void testNormalizedExcludesEmptyStrings() throws Exception {
        final Job j = Job.newBuilder().setName("x").setImage("x").setVersion("x").setRegistrationDomain("").build();
        assertFalse(Json.asNormalizedString(j).contains("registrationDomain"));
    }

    @Test
    public void verifyBuilder() throws Exception {
        final Job.Builder builder = Job.newBuilder();

        // Input to setXXX
        final String setName = "set_name";
        final String setVersion = "set_version";
        final String setImage = "set_image";
        final String setHostname = "set_hostname";
        final List<String> setCommand = asList("set", "command");
        final Map<String, String> setEnv = ImmutableMap.of("set", "env");
        final Map<String, PortMapping> setPorts = ImmutableMap.of("set_ports", PortMapping.of(1234));
        final ImmutableMap.Builder<String, ServicePortParameters> setServicePortsBuilder = ImmutableMap.builder();
        setServicePortsBuilder.put("set_ports1", new ServicePortParameters(ImmutableList.of("tag1", "tag2")));
        setServicePortsBuilder.put("set_ports2", new ServicePortParameters(ImmutableList.of("tag3", "tag4")));
        final ServicePorts setServicePorts = new ServicePorts(setServicePortsBuilder.build());
        final Map<ServiceEndpoint, ServicePorts> setRegistration = ImmutableMap
                .of(ServiceEndpoint.of("set_service", "set_proto"), setServicePorts);
        final Integer setGracePeriod = 120;
        final Map<String, String> setVolumes = ImmutableMap.of("/set", "/volume");
        final Date setExpires = new Date();
        final String setRegistrationDomain = "my.domain";
        final String setCreatingUser = "username";
        final Resources setResources = new Resources(10485760L, 10485761L, 4L, "1");
        final HealthCheck setHealthCheck = HealthCheck.newHttpHealthCheck().setPath("/healthcheck")
                .setPort("set_ports").build();
        final List<String> setSecurityOpt = Lists.newArrayList("label:user:dxia", "apparmor:foo");
        final String setNetworkMode = "host";
        final Map<String, String> setMetadata = ImmutableMap.of("set_metadata_key", "set_metadata_val");
        final Set<String> setAddCapabilities = ImmutableSet.of("set_cap_add1", "set_cap_add2");
        final Set<String> setDropCapabilities = ImmutableSet.of("set_cap_drop1", "set_cap_drop2");

        // Input to addXXX
        final Map<String, String> addEnv = ImmutableMap.of("add", "env");
        final Map<String, PortMapping> addPorts = ImmutableMap.of("add_ports", PortMapping.of(4711));
        final ImmutableMap.Builder<String, ServicePortParameters> addServicePortsBuilder = ImmutableMap.builder();
        addServicePortsBuilder.put("add_ports1", new ServicePortParameters(ImmutableList.of("tag1", "tag2")));
        addServicePortsBuilder.put("add_ports2", new ServicePortParameters(ImmutableList.of("tag3", "tag4")));
        final ServicePorts addServicePorts = new ServicePorts(addServicePortsBuilder.build());
        final Map<ServiceEndpoint, ServicePorts> addRegistration = ImmutableMap
                .of(ServiceEndpoint.of("add_service", "add_proto"), addServicePorts);
        final Map<String, String> addVolumes = ImmutableMap.of("/add", "/volume");
        final Map<String, String> addMetadata = ImmutableMap.of("add_metadata_key", "add_metadata_val");

        // Expected output from getXXX
        final String expectedName = setName;
        final String expectedVersion = setVersion;
        final String expectedImage = setImage;
        final String expectedHostname = setHostname;
        final List<String> expectedCommand = setCommand;
        final Map<String, String> expectedEnv = concat(setEnv, addEnv);
        final Map<String, PortMapping> expectedPorts = concat(setPorts, addPorts);
        final Map<ServiceEndpoint, ServicePorts> expectedRegistration = concat(setRegistration, addRegistration);
        final Integer expectedGracePeriod = setGracePeriod;
        final Map<String, String> expectedVolumes = concat(setVolumes, addVolumes);
        final Date expectedExpires = setExpires;
        final String expectedRegistrationDomain = setRegistrationDomain;
        final String expectedCreatingUser = setCreatingUser;
        final Resources expectedResources = setResources;
        final HealthCheck expectedHealthCheck = setHealthCheck;
        final List<String> expectedSecurityOpt = setSecurityOpt;
        final String expectedNetworkMode = setNetworkMode;
        final Map<String, String> expectedMetadata = concat(setMetadata, addMetadata);
        final Set<String> expectedAddCapabilities = setAddCapabilities;
        final Set<String> expectedDropCapabilities = setDropCapabilities;

        // Check setXXX methods
        builder.setName(setName);
        builder.setVersion(setVersion);
        builder.setImage(setImage);
        builder.setHostname(setHostname);
        builder.setCommand(setCommand);
        builder.setEnv(setEnv);
        builder.setPorts(setPorts);
        builder.setRegistration(setRegistration);
        builder.setGracePeriod(setGracePeriod);
        builder.setVolumes(setVolumes);
        builder.setExpires(setExpires);
        builder.setRegistrationDomain(setRegistrationDomain);
        builder.setCreatingUser(setCreatingUser);
        builder.setResources(setResources);
        builder.setHealthCheck(setHealthCheck);
        builder.setSecurityOpt(setSecurityOpt);
        builder.setNetworkMode(setNetworkMode);
        builder.setMetadata(setMetadata);
        builder.setAddCapabilities(setAddCapabilities);
        builder.setDropCapabilities(setDropCapabilities);

        // Check addXXX methods
        for (final Map.Entry<String, String> entry : addEnv.entrySet()) {
            builder.addEnv(entry.getKey(), entry.getValue());
        }
        for (final Map.Entry<String, PortMapping> entry : addPorts.entrySet()) {
            builder.addPort(entry.getKey(), entry.getValue());
        }
        for (final Map.Entry<ServiceEndpoint, ServicePorts> entry : addRegistration.entrySet()) {
            builder.addRegistration(entry.getKey(), entry.getValue());
        }
        for (final Map.Entry<String, String> entry : addVolumes.entrySet()) {
            builder.addVolume(entry.getKey(), entry.getValue());
        }
        for (final Map.Entry<String, String> entry : addMetadata.entrySet()) {
            builder.addMetadata(entry.getKey(), entry.getValue());
        }

        assertEquals("name", expectedName, builder.getName());
        assertEquals("version", expectedVersion, builder.getVersion());
        assertEquals("image", expectedImage, builder.getImage());
        assertEquals("hostname", expectedHostname, builder.getHostname());
        assertEquals("command", expectedCommand, builder.getCommand());
        assertEquals("env", expectedEnv, builder.getEnv());
        assertEquals("ports", expectedPorts, builder.getPorts());
        assertEquals("registration", expectedRegistration, builder.getRegistration());
        assertEquals("gracePeriod", expectedGracePeriod, builder.getGracePeriod());
        assertEquals("volumes", expectedVolumes, builder.getVolumes());
        assertEquals("expires", expectedExpires, builder.getExpires());
        assertEquals("registrationDomain", expectedRegistrationDomain, builder.getRegistrationDomain());
        assertEquals("creatingUser", expectedCreatingUser, builder.getCreatingUser());
        assertEquals("resources", expectedResources, builder.getResources());
        assertEquals("healthCheck", expectedHealthCheck, builder.getHealthCheck());
        assertEquals("securityOpt", expectedSecurityOpt, builder.getSecurityOpt());
        assertEquals("networkMode", expectedNetworkMode, builder.getNetworkMode());
        assertEquals("metadata", expectedMetadata, builder.getMetadata());
        assertEquals("addCapabilities", expectedAddCapabilities, builder.getAddCapabilities());
        assertEquals("dropCapabilities", expectedDropCapabilities, builder.getDropCapabilities());

        // Check final output
        final Job job = builder.build();
        assertEquals("name", expectedName, job.getId().getName());
        assertEquals("version", expectedVersion, job.getId().getVersion());
        assertEquals("image", expectedImage, job.getImage());
        assertEquals("hostname", expectedHostname, job.getHostname());
        assertEquals("command", expectedCommand, job.getCommand());
        assertEquals("env", expectedEnv, job.getEnv());
        assertEquals("ports", expectedPorts, job.getPorts());
        assertEquals("registration", expectedRegistration, job.getRegistration());
        assertEquals("gracePeriod", expectedGracePeriod, job.getGracePeriod());
        assertEquals("volumes", expectedVolumes, job.getVolumes());
        assertEquals("expires", expectedExpires, job.getExpires());
        assertEquals("registrationDomain", expectedRegistrationDomain, job.getRegistrationDomain());
        assertEquals("creatingUser", expectedCreatingUser, job.getCreatingUser());
        assertEquals("resources", expectedResources, job.getResources());
        assertEquals("healthCheck", expectedHealthCheck, job.getHealthCheck());
        assertEquals("securityOpt", expectedSecurityOpt, job.getSecurityOpt());
        assertEquals("networkMode", expectedNetworkMode, job.getNetworkMode());
        assertEquals("metadata", expectedMetadata, job.getMetadata());
        assertEquals("addCapabilities", expectedAddCapabilities, job.getAddCapabilities());
        assertEquals("dropCapabilities", expectedDropCapabilities, job.getDropCapabilities());

        // Check toBuilder
        final Job.Builder rebuilder = job.toBuilder();
        assertEquals("name", expectedName, rebuilder.getName());
        assertEquals("version", expectedVersion, rebuilder.getVersion());
        assertEquals("image", expectedImage, rebuilder.getImage());
        assertEquals("hostname", expectedHostname, rebuilder.getHostname());
        assertEquals("command", expectedCommand, rebuilder.getCommand());
        assertEquals("env", expectedEnv, rebuilder.getEnv());
        assertEquals("ports", expectedPorts, rebuilder.getPorts());
        assertEquals("registration", expectedRegistration, rebuilder.getRegistration());
        assertEquals("gracePeriod", expectedGracePeriod, rebuilder.getGracePeriod());
        assertEquals("volumes", expectedVolumes, rebuilder.getVolumes());
        assertEquals("expires", expectedExpires, rebuilder.getExpires());
        assertEquals("registrationDomain", expectedRegistrationDomain, rebuilder.getRegistrationDomain());
        assertEquals("creatingUser", expectedCreatingUser, rebuilder.getCreatingUser());
        assertEquals("resources", expectedResources, rebuilder.getResources());
        assertEquals("healthCheck", expectedHealthCheck, rebuilder.getHealthCheck());
        assertEquals("securityOpt", expectedSecurityOpt, rebuilder.getSecurityOpt());
        assertEquals("networkMode", expectedNetworkMode, rebuilder.getNetworkMode());
        assertEquals("metadata", expectedMetadata, rebuilder.getMetadata());
        assertEquals("addCapabilities", expectedAddCapabilities, rebuilder.getAddCapabilities());
        assertEquals("dropCapabilities", expectedDropCapabilities, rebuilder.getDropCapabilities());

        // Check clone
        final Job.Builder cloned = builder.clone();
        assertEquals("name", expectedName, cloned.getName());
        assertEquals("version", expectedVersion, cloned.getVersion());
        assertEquals("image", expectedImage, cloned.getImage());
        assertEquals("hostname", expectedHostname, cloned.getHostname());
        assertEquals("command", expectedCommand, cloned.getCommand());
        assertEquals("env", expectedEnv, cloned.getEnv());
        assertEquals("ports", expectedPorts, cloned.getPorts());
        assertEquals("registration", expectedRegistration, cloned.getRegistration());
        assertEquals("gracePeriod", expectedGracePeriod, cloned.getGracePeriod());
        assertEquals("volumes", expectedVolumes, cloned.getVolumes());
        assertEquals("expires", expectedExpires, cloned.getExpires());
        assertEquals("registrationDomain", expectedRegistrationDomain, cloned.getRegistrationDomain());
        assertEquals("creatingUser", expectedCreatingUser, cloned.getCreatingUser());
        assertEquals("resources", expectedResources, cloned.getResources());
        assertEquals("healthCheck", expectedHealthCheck, cloned.getHealthCheck());
        assertEquals("securityOpt", expectedSecurityOpt, cloned.getSecurityOpt());
        assertEquals("networkMode", expectedNetworkMode, cloned.getNetworkMode());
        assertEquals("metadata", expectedMetadata, cloned.getMetadata());
        assertEquals("addCapabilities", expectedAddCapabilities, cloned.getAddCapabilities());
        assertEquals("dropCapabilities", expectedDropCapabilities, cloned.getDropCapabilities());

        final Job clonedJob = cloned.build();
        assertEquals("name", expectedName, clonedJob.getId().getName());
        assertEquals("version", expectedVersion, clonedJob.getId().getVersion());
        assertEquals("image", expectedImage, clonedJob.getImage());
        assertEquals("hostname", expectedHostname, clonedJob.getHostname());
        assertEquals("command", expectedCommand, clonedJob.getCommand());
        assertEquals("env", expectedEnv, clonedJob.getEnv());
        assertEquals("ports", expectedPorts, clonedJob.getPorts());
        assertEquals("registration", expectedRegistration, clonedJob.getRegistration());
        assertEquals("gracePeriod", expectedGracePeriod, clonedJob.getGracePeriod());
        assertEquals("volumes", expectedVolumes, clonedJob.getVolumes());
        assertEquals("expires", expectedExpires, clonedJob.getExpires());
        assertEquals("registrationDomain", expectedRegistrationDomain, clonedJob.getRegistrationDomain());
        assertEquals("creatingUser", expectedCreatingUser, clonedJob.getCreatingUser());
        assertEquals("resources", expectedResources, clonedJob.getResources());
        assertEquals("healthCheck", expectedHealthCheck, clonedJob.getHealthCheck());
        assertEquals("securityOpt", expectedSecurityOpt, clonedJob.getSecurityOpt());
        assertEquals("networkMode", expectedNetworkMode, clonedJob.getNetworkMode());
        assertEquals("metadata", expectedMetadata, clonedJob.getMetadata());
        assertEquals("addCapabilities", expectedAddCapabilities, clonedJob.getAddCapabilities());
        assertEquals("dropCapabilities", expectedDropCapabilities, clonedJob.getDropCapabilities());
    }

    @SafeVarargs
    private final <K, V> Map<K, V> concat(final Map<K, V>... maps) {
        final ImmutableMap.Builder<K, V> b = ImmutableMap.builder();
        for (final Map<K, V> map : maps) {
            b.putAll(map);
        }
        return b.build();
    }

    /** Verify the Builder allows calling addFoo() before setFoo() for collection types. */
    @Test
    public void testBuilderAddBeforeSet() throws Exception {
        final Job job = Job.newBuilder().addEnv("env", "var").addMetadata("meta", "data")
                .addPort("http", PortMapping.of(80, 8000))
                .addRegistration(ServiceEndpoint.of("foo", "http"), ServicePorts.of("http"))
                .addVolume("/foo", "/bar").build();

        assertThat(job.getEnv(), hasEntry("env", "var"));
        assertThat(job.getMetadata(), hasEntry("meta", "data"));
        assertThat(job.getPorts(), hasEntry("http", PortMapping.of(80, 8000)));
        assertThat(job.getRegistration(), hasEntry(ServiceEndpoint.of("foo", "http"), ServicePorts.of("http")));
        assertThat(job.getVolumes(), hasEntry("/foo", "/bar"));
    }

    @Test
    public void verifySha1ID() throws IOException {
        final Map<String, Object> expectedConfig = map("command", asList("foo", "bar"), "image", "foobar:4711",
                "name", "foozbarz", "version", "17");

        final String expectedInput = "foozbarz:17:" + hex(Json.sha1digest(expectedConfig));
        final String expectedDigest = hex(Hash.sha1digest(expectedInput.getBytes(UTF_8)));
        final JobId expectedId = JobId.fromString("foozbarz:17:" + expectedDigest);

        final Job job = Job.newBuilder().setCommand(asList("foo", "bar")).setImage("foobar:4711")
                .setName("foozbarz").setVersion("17").build();

        assertEquals(expectedId, job.getId());
    }

    @Test
    public void verifySha1IDWithEnv() throws IOException {
        final Map<String, String> env = ImmutableMap.of("FOO", "BAR");
        final Map<String, Object> expectedConfig = map("command", asList("foo", "bar"), "image", "foobar:4711",
                "name", "foozbarz", "version", "17", "env", env);

        final String expectedInput = "foozbarz:17:" + hex(Json.sha1digest(expectedConfig));
        final String expectedDigest = hex(Hash.sha1digest(expectedInput.getBytes(UTF_8)));
        final JobId expectedId = JobId.fromString("foozbarz:17:" + expectedDigest);

        final Job job = Job.newBuilder().setCommand(asList("foo", "bar")).setImage("foobar:4711")
                .setName("foozbarz").setVersion("17").setEnv(env).build();

        assertEquals(expectedId, job.getId());
    }

    private String hex(final byte[] bytes) {
        return BaseEncoding.base16().lowerCase().encode(bytes);
    }

    @Test
    public void verifyCanParseJobWithUnknownFields() throws Exception {
        final Job job = Job.newBuilder().setCommand(asList("foo", "bar")).setImage("foobar:4711")
                .setName("foozbarz").setVersion("17").build();

        final String jobJson = job.toJsonString();

        final ObjectMapper objectMapper = new ObjectMapper();
        final Map<String, Object> fields = objectMapper.readValue(jobJson,
                new TypeReference<Map<String, Object>>() {
                });
        fields.put("UNKNOWN_FIELD", "FOOBAR");
        final String modifiedJobJson = objectMapper.writeValueAsString(fields);

        final Job parsedJob = parse(modifiedJobJson, Job.class);

        assertEquals(job, parsedJob);
    }

    @Test
    public void verifyCanParseJobWithMissingEnv() throws Exception {
        final Job job = Job.newBuilder().setCommand(asList("foo", "bar")).setImage("foobar:4711")
                .setName("foozbarz").setVersion("17").build();

        removeFieldAndParse(job, "env");
    }

    @Test
    public void verifyCanParseJobWithMissingMetadata() throws Exception {
        final Job job = Job.newBuilder().setCommand(asList("foo", "bar")).setImage("foobar:4711")
                .setName("foozbarz").setVersion("17").build();

        removeFieldAndParse(job, "metadata");
    }

    private static void removeFieldAndParse(final Job job, final String... fieldNames) throws Exception {
        final String jobJson = job.toJsonString();

        final ObjectMapper objectMapper = new ObjectMapper();
        final Map<String, Object> fields = objectMapper.readValue(jobJson,
                new TypeReference<Map<String, Object>>() {
                });

        for (final String field : fieldNames) {
            fields.remove(field);
        }
        final String modifiedJobJson = objectMapper.writeValueAsString(fields);

        final Job parsedJob = parse(modifiedJobJson, Job.class);

        assertEquals(job, parsedJob);
    }

    @Test
    public void verifyJobIsImmutable() {
        final List<String> expectedCommand = ImmutableList.of("foo");
        final Map<String, String> expectedEnv = ImmutableMap.of("e1", "1");
        final Map<String, String> expectedMetadata = ImmutableMap.of("foo", "bar");
        final Map<String, PortMapping> expectedPorts = ImmutableMap.of("p1", PortMapping.of(1, 2));
        final Map<ServiceEndpoint, ServicePorts> expectedRegistration = ImmutableMap
                .of(ServiceEndpoint.of("foo", "tcp"), ServicePorts.of("p1"));
        final Integer expectedGracePeriod = 240;

        final List<String> mutableCommand = Lists.newArrayList(expectedCommand);
        final Map<String, String> mutableEnv = Maps.newHashMap(expectedEnv);
        final Map<String, String> mutableMetadata = Maps.newHashMap(expectedMetadata);
        final Map<String, PortMapping> mutablePorts = Maps.newHashMap(expectedPorts);
        final Map<ServiceEndpoint, ServicePorts> mutableRegistration = Maps.newHashMap(expectedRegistration);

        final Job.Builder builder = Job.newBuilder().setCommand(mutableCommand).setEnv(mutableEnv)
                .setMetadata(mutableMetadata).setPorts(mutablePorts).setImage("foobar:4711").setName("foozbarz")
                .setVersion("17").setRegistration(mutableRegistration).setGracePeriod(expectedGracePeriod);

        final Job job = builder.build();

        mutableCommand.add("bar");
        mutableEnv.put("e2", "2");
        mutableMetadata.put("some", "thing");
        mutablePorts.put("p2", PortMapping.of(3, 4));
        mutableRegistration.put(ServiceEndpoint.of("bar", "udp"), ServicePorts.of("p2"));

        builder.addEnv("added_env", "FOO");
        builder.addMetadata("added", "data");
        builder.addPort("added_port", PortMapping.of(4711));
        builder.addRegistration(ServiceEndpoint.of("added_reg", "added_proto"), ServicePorts.of("added_port"));
        builder.setGracePeriod(480);

        assertEquals(expectedCommand, job.getCommand());
        assertEquals(expectedEnv, job.getEnv());
        assertEquals(expectedMetadata, job.getMetadata());
        assertEquals(expectedPorts, job.getPorts());
        assertEquals(expectedRegistration, job.getRegistration());
        assertEquals(expectedGracePeriod, job.getGracePeriod());
    }

    @Test
    public void testChangingPortTagsChangesJobHash() {
        final Job j = Job.newBuilder().setName("foo").setVersion("1").setImage("foobar").build();
        final Job.Builder builder = j.toBuilder();
        final Map<String, PortMapping> ports = ImmutableMap.of("add_ports1", PortMapping.of(1234), "add_ports2",
                PortMapping.of(2345));
        final ImmutableMap.Builder<String, ServicePortParameters> servicePortsBuilder = ImmutableMap.builder();
        servicePortsBuilder.put("add_ports1", new ServicePortParameters(ImmutableList.of("tag1", "tag2")));
        servicePortsBuilder.put("add_ports2", new ServicePortParameters(ImmutableList.of("tag3", "tag4")));
        final ServicePorts servicePorts = new ServicePorts(servicePortsBuilder.build());
        final Map<ServiceEndpoint, ServicePorts> oldRegistration = ImmutableMap
                .of(ServiceEndpoint.of("add_service", "add_proto"), servicePorts);
        final Job job = builder.setPorts(ports).setRegistration(oldRegistration).build();

        final ImmutableMap.Builder<String, ServicePortParameters> newServicePortsBuilder = ImmutableMap.builder();
        newServicePortsBuilder.put("add_ports1", new ServicePortParameters(ImmutableList.of("tag1", "newtag")));
        newServicePortsBuilder.put("add_ports2", new ServicePortParameters(ImmutableList.of("tag3", "tag4")));
        final ServicePorts newServicePorts = new ServicePorts(newServicePortsBuilder.build());
        final Map<ServiceEndpoint, ServicePorts> newRegistration = ImmutableMap
                .of(ServiceEndpoint.of("add_service", "add_proto"), newServicePorts);
        final Job newJob = builder.setRegistration(newRegistration).build();

        assertNotEquals(job.getId().getHash(), newJob.getId().getHash());
    }

    @Test
    public void testBuildWithoutHash() {
        final Job.Builder builder = Job.newBuilder().setCommand(asList("foo", "bar")).setImage("foobar:4711")
                .setName("foozbarz").setVersion("17");

        assertNull(builder.buildWithoutHash().getId().getHash());
        assertNotNull(builder.build().getId().getHash());
    }
}