Initial push of API v3 test utility.
diff --git a/utils/utils.go b/utils/utils.go
new file mode 100644
index 0000000..ea9f1fa
--- /dev/null
+++ b/utils/utils.go
@@ -0,0 +1,147 @@
+/*
+Copyright 2017 Google Inc.
+
+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
+
+https://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 utils contains common BookingService based helper functions.
+package utils
+
+import (
+	"errors"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"math/rand"
+	"path"
+	"strings"
+
+	"github.com/golang/protobuf/jsonpb"
+	"github.com/golang/protobuf/proto"
+	"github.com/google/go-cmp/cmp"
+
+	fpb "github.com/maps-booking/feeds"
+	mpb "github.com/maps-booking/v3"
+)
+
+// SlotKey is a struct representing a unique service.
+type SlotKey struct {
+	MerchantID string
+	ServiceID  string
+	StaffID    string
+	RoomID     string
+}
+
+// LogFlow is a convenience function for logging common flows..
+func LogFlow(f string, status string) {
+	log.Println(strings.Join([]string{"\n##########\n", status, f, "Flow", "\n##########"}, " "))
+}
+
+// AvailabilityFrom parses the file specified in availabilityFeed, returning a random permutation of availability data, maximum entries specified in testSlots
+func AvailabilityFrom(availabilityFeed string, testSlots int) ([]*fpb.Availability, error) {
+	LogFlow("Parse Input Feed", "Start")
+	defer LogFlow("Parse Input Feed", "End")
+
+	var feed fpb.AvailabilityFeed
+	content, err := ioutil.ReadFile(availabilityFeed)
+	if err != nil {
+		return nil, fmt.Errorf("unable to read input file: %v", err)
+	}
+	if path.Ext(availabilityFeed) == ".json" {
+		if err := jsonpb.UnmarshalString(string(content), &feed); err != nil {
+			return nil, fmt.Errorf("unable to parse feed as json: %v", err)
+		}
+	}
+	if path.Ext(availabilityFeed) == ".pb3" {
+		if err := proto.Unmarshal(content, &feed); err != nil {
+			return nil, fmt.Errorf("unable to parse feed as pb3: %v", err)
+		}
+	}
+
+	var finalAvailability []*fpb.Availability
+	var rawAvailability []*fpb.Availability
+	for _, sa := range feed.GetServiceAvailability() {
+		rawAvailability = append(rawAvailability, sa.GetAvailability()...)
+	}
+	if len(rawAvailability) == 0 || testSlots == 0 {
+		return finalAvailability, errors.New("no valid availability in feed, exiting workflows")
+	}
+	if len(rawAvailability) <= testSlots {
+		finalAvailability = rawAvailability
+	} else {
+		nums := rand.Perm(len(rawAvailability))[0:testSlots]
+		for _, n := range nums {
+			finalAvailability = append(finalAvailability, rawAvailability[n])
+		}
+	}
+	log.Printf("Selected %d slots out of a possible %d", len(finalAvailability), len(rawAvailability))
+	return finalAvailability, nil
+}
+
+// ValidateBooking performs granular comparisons between all got and want Bookings.
+func ValidateBooking(got *mpb.Booking, want *mpb.Booking) error {
+	if got.GetBookingId() == "" {
+		return errors.New("booking_id is empty")
+	}
+	if diff := cmp.Diff(got.GetSlot(), want.GetSlot(), cmp.Comparer(proto.Equal)); diff != "" {
+		return fmt.Errorf("slots differ (-got +want)\n%s", diff)
+	}
+	// UserId is the only required field for the partner to return.
+	if diff := cmp.Diff(got.GetUserInformation().GetUserId(), want.GetUserInformation().GetUserId()); diff != "" {
+		return fmt.Errorf("users differ (-got +want)\n%s", diff)
+	}
+	if diff := cmp.Diff(got.GetPaymentInformation(), want.GetPaymentInformation(), cmp.Comparer(proto.Equal)); diff != "" {
+		return fmt.Errorf("payment information differs (-got +want)\n%s", diff)
+	}
+	// BookingStatus_CONFIRMED is the default case unless want overrides it.
+	wantStatus := mpb.BookingStatus_CONFIRMED
+	if want.GetStatus() != mpb.BookingStatus_BOOKING_STATUS_UNSPECIFIED {
+		wantStatus = want.GetStatus()
+	}
+	if diff := cmp.Diff(got.GetStatus(), wantStatus); diff != "" {
+		return fmt.Errorf("status differs (-got +want)\n%s", diff)
+	}
+	return nil
+}
+
+// BuildSlotFrom creates a bookingservice slot from an feed availability record.
+func BuildSlotFrom(a *fpb.Availability) (*mpb.Slot, error) {
+	r := a.GetResources()
+	return &mpb.Slot{
+		MerchantId:      a.GetMerchantId(),
+		ServiceId:       a.GetServiceId(),
+		StartSec:        a.GetStartSec(),
+		DurationSec:     a.GetDurationSec(),
+		AvailabilityTag: a.GetAvailabilityTag(),
+		Resources: &mpb.ResourceIds{
+			StaffId:   r.GetStaffId(),
+			RoomId:    r.GetRoomId(),
+			PartySize: r.GetPartySize(),
+		},
+	}, nil
+}
+
+// BuildMerchantServiceMap creates a key value pair of unique services to all of their availability slots.
+func BuildMerchantServiceMap(av []*fpb.Availability) map[SlotKey][]*fpb.Availability {
+	m := make(map[SlotKey][]*fpb.Availability)
+	for _, a := range av {
+		key := SlotKey{
+			MerchantID: a.GetMerchantId(),
+			ServiceID:  a.GetServiceId(),
+			StaffID:    a.GetResources().GetStaffId(),
+			RoomID:     a.GetResources().GetRoomId(),
+		}
+		m[key] = append(m[key], a)
+	}
+	return m
+}