This is an automated email from the ASF dual-hosted git repository.

dangogh pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-trafficcontrol.git

commit b4a5f0050fd3d0dfc405823aae593b2bf63d93f3
Author: Dewayne Richardson <dewr...@apache.org>
AuthorDate: Fri Mar 2 13:24:23 2018 -0700

    initial version of the source for /types
---
 traffic_ops/client/v13/type.go                     | 132 ++++++++
 traffic_ops/testing/api/v13/types_test.go          | 109 +++++++
 traffic_ops/traffic_ops_golang/types/types.go      | 339 +++++++++++++++++++++
 traffic_ops/traffic_ops_golang/types/types_test.go | 146 +++++++++
 4 files changed, 726 insertions(+)

diff --git a/traffic_ops/client/v13/type.go b/traffic_ops/client/v13/type.go
new file mode 100644
index 0000000..0c95d0a
--- /dev/null
+++ b/traffic_ops/client/v13/type.go
@@ -0,0 +1,132 @@
+/*
+
+   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 v13
+
+import (
+       "encoding/json"
+       "fmt"
+       "net"
+       "net/http"
+
+       "github.com/apache/incubator-trafficcontrol/lib/go-tc"
+)
+
+const (
+       API_v13_Types = "/api/1.3/types"
+)
+
+// Create a Type
+func (to *Session) CreateType(typ tc.Type) (tc.Alerts, ReqInf, error) {
+
+       var remoteAddr net.Addr
+       reqBody, err := json.Marshal(typ)
+       reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: 
remoteAddr}
+       if err != nil {
+               return tc.Alerts{}, reqInf, err
+       }
+       resp, remoteAddr, err := to.request(http.MethodPost, API_v13_Types, 
reqBody)
+       if err != nil {
+               return tc.Alerts{}, reqInf, err
+       }
+       defer resp.Body.Close()
+       var alerts tc.Alerts
+       err = json.NewDecoder(resp.Body).Decode(&alerts)
+       return alerts, reqInf, nil
+}
+
+// Update a Type by ID
+func (to *Session) UpdateTypeByID(id int, typ tc.Type) (tc.Alerts, ReqInf, 
error) {
+
+       var remoteAddr net.Addr
+       reqBody, err := json.Marshal(typ)
+       reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: 
remoteAddr}
+       if err != nil {
+               return tc.Alerts{}, reqInf, err
+       }
+       route := fmt.Sprintf("%s/%d", API_v13_Types, id)
+       resp, remoteAddr, err := to.request(http.MethodPut, route, reqBody)
+       if err != nil {
+               return tc.Alerts{}, reqInf, err
+       }
+       defer resp.Body.Close()
+       var alerts tc.Alerts
+       err = json.NewDecoder(resp.Body).Decode(&alerts)
+       return alerts, reqInf, nil
+}
+
+// Returns a list of Types
+func (to *Session) GetTypes() ([]tc.Type, ReqInf, error) {
+       resp, remoteAddr, err := to.request(http.MethodGet, API_v13_Types, nil)
+       reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: 
remoteAddr}
+       if err != nil {
+               return nil, reqInf, err
+       }
+       defer resp.Body.Close()
+
+       var data tc.TypesResponse
+       err = json.NewDecoder(resp.Body).Decode(&data)
+       return data.Response, reqInf, nil
+}
+
+// GET a Type by the Type ID
+func (to *Session) GetTypeByID(id int) ([]tc.Type, ReqInf, error) {
+       route := fmt.Sprintf("%s/%d", API_v13_Types, id)
+       resp, remoteAddr, err := to.request(http.MethodGet, route, nil)
+       reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: 
remoteAddr}
+       if err != nil {
+               return nil, reqInf, err
+       }
+       defer resp.Body.Close()
+
+       var data tc.TypesResponse
+       if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
+               return nil, reqInf, err
+       }
+
+       return data.Response, reqInf, nil
+}
+
+// GET a Type by the Type name
+func (to *Session) GetTypeByName(name string) ([]tc.Type, ReqInf, error) {
+       url := fmt.Sprintf("%s?name=%s", API_v13_Types, name)
+       resp, remoteAddr, err := to.request(http.MethodGet, url, nil)
+       reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: 
remoteAddr}
+       if err != nil {
+               return nil, reqInf, err
+       }
+       defer resp.Body.Close()
+
+       var data tc.TypesResponse
+       if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
+               return nil, reqInf, err
+       }
+
+       return data.Response, reqInf, nil
+}
+
+// DELETE a Type by ID
+func (to *Session) DeleteTypeByID(id int) (tc.Alerts, ReqInf, error) {
+       route := fmt.Sprintf("%s/%d", API_v13_Types, id)
+       resp, remoteAddr, err := to.request(http.MethodDelete, route, nil)
+       reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: 
remoteAddr}
+       if err != nil {
+               return tc.Alerts{}, reqInf, err
+       }
+       defer resp.Body.Close()
+       var alerts tc.Alerts
+       err = json.NewDecoder(resp.Body).Decode(&alerts)
+       return alerts, reqInf, nil
+}
diff --git a/traffic_ops/testing/api/v13/types_test.go 
b/traffic_ops/testing/api/v13/types_test.go
new file mode 100644
index 0000000..764501a
--- /dev/null
+++ b/traffic_ops/testing/api/v13/types_test.go
@@ -0,0 +1,109 @@
+package v13
+
+/*
+
+   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.
+*/
+
+import (
+       "testing"
+
+       "github.com/apache/incubator-trafficcontrol/lib/go-log"
+       tc "github.com/apache/incubator-trafficcontrol/lib/go-tc"
+)
+
+func TestTypes(t *testing.T) {
+
+       CreateTestTypes(t)
+       UpdateTestTypes(t)
+       //GetTestTypes(t)
+       //DeleteTestTypes(t)
+
+}
+
+func CreateTestTypes(t *testing.T) {
+
+       for _, typ := range testData.Types {
+               resp, _, err := TOSession.CreateType(typ)
+               log.Debugln("Response: ", resp)
+               if err != nil {
+                       t.Errorf("could not CREATE types: %v\n", err)
+               }
+       }
+
+}
+
+func UpdateTestTypes(t *testing.T) {
+
+       firstType := testData.Types[0]
+       // Retrieve the Type by name so we can get the id for the Update
+       resp, _, err := TOSession.GetTypeByName(firstType.Name)
+       if err != nil {
+               t.Errorf("cannot GET Type by name: %v - %v\n", firstType.Name, 
err)
+       }
+       remoteType := resp[0]
+       expectedTypeName := "testType1"
+       remoteType.Name = expectedTypeName
+       var alert tc.Alerts
+       alert, _, err = TOSession.UpdateTypeByID(remoteType.ID, remoteType)
+       if err != nil {
+               t.Errorf("cannot UPDATE Type by id: %v - %v\n", err, alert)
+       }
+
+       // Retrieve the Type to check Type name got updated
+       resp, _, err = TOSession.GetTypeByID(remoteType.ID)
+       if err != nil {
+               t.Errorf("cannot GET Type by name: %v - %v\n", firstType.Name, 
err)
+       }
+       respType := resp[0]
+       if respType.Name != expectedTypeName {
+               t.Errorf("results do not match actual: %s, expected: %s\n", 
respType.Name, expectedTypeName)
+       }
+
+}
+
+func GetTestTypes(t *testing.T) {
+
+       for _, typ := range testData.Types {
+               resp, _, err := TOSession.GetTypeByName(typ.Name)
+               if err != nil {
+                       t.Errorf("cannot GET Type by name: %v - %v\n", err, 
resp)
+               }
+       }
+}
+
+func DeleteTestTypes(t *testing.T) {
+
+       for _, typ := range testData.Types {
+               // Retrieve the Type by name so we can get the id for the Update
+               resp, _, err := TOSession.GetTypeByName(typ.Name)
+               if err != nil {
+                       t.Errorf("cannot GET Type by name: %v - %v\n", 
typ.Name, err)
+               }
+               respType := resp[0]
+
+               delResp, _, err := TOSession.DeleteTypeByID(respType.ID)
+               if err != nil {
+                       t.Errorf("cannot DELETE Type by name: %v - %v\n", err, 
delResp)
+               }
+
+               // Retrieve the Type to see if it got deleted
+               types, _, err := TOSession.GetTypeByName(typ.Name)
+               if err != nil {
+                       t.Errorf("error deleting Type name: %s\n", err.Error())
+               }
+               if len(types) > 0 {
+                       t.Errorf("expected Type name: %s to be deleted\n", 
typ.Name)
+               }
+       }
+}
diff --git a/traffic_ops/traffic_ops_golang/types/types.go 
b/traffic_ops/traffic_ops_golang/types/types.go
new file mode 100644
index 0000000..41811b4
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/types/types.go
@@ -0,0 +1,339 @@
+package types
+
+/*
+ * Licensed to 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.
+ */
+
+import (
+       "errors"
+       "fmt"
+       "strconv"
+
+       "github.com/apache/incubator-trafficcontrol/lib/go-log"
+       "github.com/apache/incubator-trafficcontrol/lib/go-tc"
+       
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/api"
+       
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/auth"
+       
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers"
+       
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/tovalidate"
+       validation "github.com/go-ozzo/ozzo-validation"
+       "github.com/jmoiron/sqlx"
+       "github.com/lib/pq"
+)
+
+//we need a type alias to define functions on
+type TOType tc.TypeNullable
+
+//the refType is passed into the handlers where a copy of its type is used to 
decode the json.
+var refType = TOType(tc.TypeNullable{})
+
+func GetRefType() *TOType {
+       return &refType
+}
+
+//Implementation of the Identifier, Validator interface functions
+func (typ *TOType) GetID() (int, bool) {
+       if typ.ID == nil {
+               return 0, false
+       }
+       return *typ.ID, true
+}
+
+func (typ *TOType) GetAuditName() string {
+       if typ.Name != nil {
+               return *typ.Name
+       }
+       if typ.ID != nil {
+               return strconv.Itoa(*typ.ID)
+       }
+       return "unknown"
+}
+
+func (typ *TOType) GetType() string {
+       return "type"
+}
+
+func (typ *TOType) SetID(i int) {
+       typ.ID = &i
+}
+
+func (typ *TOType) Validate(db *sqlx.DB) []error {
+       errs := validation.Errors{
+               "name":         validation.Validate(typ.Name, 
validation.Required),
+               "description":  validation.Validate(typ.Description, 
validation.Required),
+               "use_in_table": validation.Validate(typ.UseInTable, 
validation.Required),
+       }
+       if errs != nil {
+               return tovalidate.ToErrors(errs)
+       }
+       return nil
+}
+
+func (typ *TOType) Read(db *sqlx.DB, parameters map[string]string, user 
auth.CurrentUser) ([]interface{}, []error, tc.ApiErrorType) {
+       var rows *sqlx.Rows
+
+       // Query Parameters to Database Query column mappings
+       // see the fields mapped in the SQL query
+       queryParamsToQueryCols := map[string]dbhelpers.WhereColumnInfo{
+               "name":       dbhelpers.WhereColumnInfo{"typ.name", nil},
+               "id":         dbhelpers.WhereColumnInfo{"typ.id", api.IsInt},
+               "useInTable": dbhelpers.WhereColumnInfo{"typ.use_in_table", 
nil},
+       }
+       where, orderBy, queryValues, errs := 
dbhelpers.BuildWhereAndOrderBy(parameters, queryParamsToQueryCols)
+       if len(errs) > 0 {
+               return nil, errs, tc.DataConflictError
+       }
+
+       query := selectQuery() + where + orderBy
+       log.Debugln("Query is ", query)
+
+       rows, err := db.NamedQuery(query, queryValues)
+       if err != nil {
+               log.Errorf("Error querying Types: %v", err)
+               return nil, []error{tc.DBError}, tc.SystemError
+       }
+       defer rows.Close()
+
+       types := []interface{}{}
+       for rows.Next() {
+               var typ tc.TypeNullable
+               if err = rows.StructScan(&typ); err != nil {
+                       log.Errorf("error parsing Type rows: %v", err)
+                       return nil, []error{tc.DBError}, tc.SystemError
+               }
+               types = append(types, typ)
+       }
+
+       return types, []error{}, tc.NoError
+
+}
+
+func selectQuery() string {
+       query := `SELECT
+id,
+name,
+description,
+use_in_table
+FROM type typ`
+
+       return query
+}
+
+//The TOType implementation of the Updater interface
+//all implementations of Updater should use transactions and return the proper 
errorType
+//ParsePQUniqueConstraintError is used to determine if a type with conflicting 
values exists
+//if so, it will return an errorType of DataConflict and the type should be 
appended to the
+//generic error message returned
+func (typ *TOType) Update(db *sqlx.DB, user auth.CurrentUser) (error, 
tc.ApiErrorType) {
+       rollbackTransaction := true
+       tx, err := db.Beginx()
+       defer func() {
+               if tx == nil || !rollbackTransaction {
+                       return
+               }
+               err := tx.Rollback()
+               if err != nil {
+                       log.Errorln(errors.New("rolling back transaction: " + 
err.Error()))
+               }
+       }()
+
+       if err != nil {
+               log.Error.Printf("could not begin transaction: %v", err)
+               return tc.DBError, tc.SystemError
+       }
+       log.Debugf("about to run exec query: %s with type: %++v", 
updateQuery(), typ)
+       resultRows, err := tx.NamedQuery(updateQuery(), typ)
+       if err != nil {
+               if pqErr, ok := err.(*pq.Error); ok {
+                       err, eType := 
dbhelpers.ParsePQUniqueConstraintError(pqErr)
+                       if eType == tc.DataConflictError {
+                               return errors.New("a type with " + 
err.Error()), eType
+                       }
+                       return err, eType
+               }
+               log.Errorf("received error: %++v from update execution", err)
+               return tc.DBError, tc.SystemError
+       }
+       defer resultRows.Close()
+
+       var lastUpdated tc.TimeNoMod
+       rowsAffected := 0
+       for resultRows.Next() {
+               rowsAffected++
+               if err := resultRows.Scan(&lastUpdated); err != nil {
+                       log.Error.Printf("could not scan lastUpdated from 
insert: %s\n", err)
+                       return tc.DBError, tc.SystemError
+               }
+       }
+       log.Debugf("lastUpdated: %++v", lastUpdated)
+       typ.LastUpdated = &lastUpdated
+       if rowsAffected != 1 {
+               if rowsAffected < 1 {
+                       return errors.New("no type found with this id"), 
tc.DataMissingError
+               }
+               return fmt.Errorf("this update affected too many rows: %d", 
rowsAffected), tc.SystemError
+       }
+       err = tx.Commit()
+       if err != nil {
+               log.Errorln("Could not commit transaction: ", err)
+               return tc.DBError, tc.SystemError
+       }
+       rollbackTransaction = false
+       return nil, tc.NoError
+}
+
+//The TOType implementation of the Creator interface
+//all implementations of Creator should use transactions and return the proper 
errorType
+//ParsePQUniqueConstraintError is used to determine if a type with conflicting 
values exists
+//if so, it will return an errorType of DataConflict and the type should be 
appended to the
+//generic error message returned
+//The insert sql returns the id and lastUpdated values of the newly inserted 
type and have
+//to be added to the struct
+func (typ *TOType) Create(db *sqlx.DB, user auth.CurrentUser) (error, 
tc.ApiErrorType) {
+       rollbackTransaction := true
+       tx, err := db.Beginx()
+       defer func() {
+               if tx == nil || !rollbackTransaction {
+                       return
+               }
+               err := tx.Rollback()
+               if err != nil {
+                       log.Errorln(errors.New("rolling back transaction: " + 
err.Error()))
+               }
+       }()
+
+       if err != nil {
+               log.Error.Printf("could not begin transaction: %v", err)
+               return tc.DBError, tc.SystemError
+       }
+       resultRows, err := tx.NamedQuery(insertQuery(), typ)
+       if err != nil {
+               if pqErr, ok := err.(*pq.Error); ok {
+                       err, eType := 
dbhelpers.ParsePQUniqueConstraintError(pqErr)
+                       if eType == tc.DataConflictError {
+                               return errors.New("a type with " + 
err.Error()), eType
+                       }
+                       return err, eType
+               }
+               log.Errorf("received non pq error: %++v from create execution", 
err)
+               return tc.DBError, tc.SystemError
+       }
+       defer resultRows.Close()
+
+       var id int
+       var lastUpdated tc.TimeNoMod
+       rowsAffected := 0
+       for resultRows.Next() {
+               rowsAffected++
+               if err := resultRows.Scan(&id, &lastUpdated); err != nil {
+                       log.Error.Printf("could not scan id from insert: %s\n", 
err)
+                       return tc.DBError, tc.SystemError
+               }
+       }
+       if rowsAffected == 0 {
+               err = errors.New("no type was inserted, no id was returned")
+               log.Errorln(err)
+               return tc.DBError, tc.SystemError
+       }
+       if rowsAffected > 1 {
+               err = errors.New("too many ids returned from type insert")
+               log.Errorln(err)
+               return tc.DBError, tc.SystemError
+       }
+
+       typ.SetID(id)
+       typ.LastUpdated = &lastUpdated
+       err = tx.Commit()
+       if err != nil {
+               log.Errorln("Could not commit transaction: ", err)
+               return tc.DBError, tc.SystemError
+       }
+       rollbackTransaction = false
+       return nil, tc.NoError
+}
+
+//The Type implementation of the Deleter interface
+//all implementations of Deleter should use transactions and return the proper 
errorType
+func (typ *TOType) Delete(db *sqlx.DB, user auth.CurrentUser) (error, 
tc.ApiErrorType) {
+       rollbackTransaction := true
+       tx, err := db.Beginx()
+       defer func() {
+               if tx == nil || !rollbackTransaction {
+                       return
+               }
+               err := tx.Rollback()
+               if err != nil {
+                       log.Errorln(errors.New("rolling back transaction: " + 
err.Error()))
+               }
+       }()
+
+       if err != nil {
+               log.Error.Printf("could not begin transaction: %v", err)
+               return tc.DBError, tc.SystemError
+       }
+       log.Debugf("about to run exec query: %s with type: %++v", 
deleteQuery(), typ)
+       result, err := tx.NamedExec(deleteQuery(), typ)
+       if err != nil {
+               log.Errorf("received error: %++v from delete execution", err)
+               return tc.DBError, tc.SystemError
+       }
+       rowsAffected, err := result.RowsAffected()
+       if err != nil {
+               return tc.DBError, tc.SystemError
+       }
+       if rowsAffected < 1 {
+               return errors.New("no type with that id found"), 
tc.DataMissingError
+       }
+       if rowsAffected > 1 {
+               return fmt.Errorf("this create affected too many rows: %d", 
rowsAffected), tc.SystemError
+       }
+
+       err = tx.Commit()
+       if err != nil {
+               log.Errorln("Could not commit transaction: ", err)
+               return tc.DBError, tc.SystemError
+       }
+       rollbackTransaction = false
+       return nil, tc.NoError
+}
+
+func updateQuery() string {
+       query := `UPDATE
+type SET
+name=:name,
+description=:description,
+use_in_table=:use_in_table
+WHERE id=:id RETURNING last_updated`
+       return query
+}
+
+func insertQuery() string {
+       query := `INSERT INTO type (
+name,
+description,
+use_in_table) VALUES (
+:name,
+:description,
+:use_in_table) RETURNING id,last_updated`
+       return query
+}
+
+func deleteQuery() string {
+       query := `DELETE FROM type
+WHERE id=:id`
+       return query
+}
diff --git a/traffic_ops/traffic_ops_golang/types/types_test.go 
b/traffic_ops/traffic_ops_golang/types/types_test.go
new file mode 100644
index 0000000..077dfa7
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/types/types_test.go
@@ -0,0 +1,146 @@
+package types
+
+/*
+ * Licensed to 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.
+ */
+
+import (
+       "errors"
+       "reflect"
+       "testing"
+       "time"
+
+       "github.com/apache/incubator-trafficcontrol/lib/go-tc"
+       
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/api"
+       
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/auth"
+       
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/test"
+       "github.com/jmoiron/sqlx"
+
+       sqlmock "gopkg.in/DATA-DOG/go-sqlmock.v1"
+)
+
+func getTestPhysLocations() []tc.PhysLocation {
+       physLocations := []tc.PhysLocation{}
+       testCase := tc.PhysLocation{
+               Address:     "1118 S. Grant St.",
+               City:        "Denver",
+               Email:       "d...@gmail.com",
+               ID:          1,
+               LastUpdated: tc.TimeNoMod{Time: time.Now()},
+               Name:        "physLocation1",
+               Phone:       "303-210-0000",
+               POC:         "Dennis Thompson",
+               RegionID:    1,
+               RegionName:  "region1",
+               ShortName:   "pl1",
+               State:       "CO",
+               Zip:         "80210",
+       }
+       physLocations = append(physLocations, testCase)
+
+       testCase2 := testCase
+       testCase2.Name = "physLocation2"
+       physLocations = append(physLocations, testCase2)
+
+       return physLocations
+}
+
+func TestGetPhysLocations(t *testing.T) {
+       mockDB, mock, err := sqlmock.New()
+       if err != nil {
+               t.Fatalf("an error '%s' was not expected when opening a stub 
database connection", err)
+       }
+       defer mockDB.Close()
+
+       db := sqlx.NewDb(mockDB, "sqlmock")
+       defer db.Close()
+
+       testCase := getTestPhysLocations()
+       cols := test.ColsFromStructByTag("db", tc.PhysLocation{})
+       rows := sqlmock.NewRows(cols)
+
+       for _, ts := range testCase {
+               rows = rows.AddRow(
+                       ts.Address,
+                       ts.City,
+                       ts.Comments,
+                       ts.Email,
+                       ts.ID,
+                       ts.LastUpdated,
+                       ts.Name,
+                       ts.Phone,
+                       ts.POC,
+                       ts.RegionID,
+                       ts.RegionName,
+                       ts.ShortName,
+                       ts.State,
+                       ts.Zip,
+               )
+       }
+       mock.ExpectQuery("SELECT").WillReturnRows(rows)
+       v := map[string]string{"dsId": "1"}
+
+       physLocations, errs, _ := refType.Read(db, v, auth.CurrentUser{})
+       if len(errs) > 0 {
+               t.Errorf("physLocation.Read expected: no errors, actual: %v", 
errs)
+       }
+
+       if len(physLocations) != 2 {
+               t.Errorf("physLocation.Read expected: len(physLocations) == 2, 
actual: %v", len(physLocations))
+       }
+
+}
+
+func TestInterfaces(t *testing.T) {
+       var i interface{}
+       i = &TOPhysLocation{}
+
+       if _, ok := i.(api.Creator); !ok {
+               t.Errorf("PhysLocation must be Creator")
+       }
+       if _, ok := i.(api.Reader); !ok {
+               t.Errorf("PhysLocation must be Reader")
+       }
+       if _, ok := i.(api.Updater); !ok {
+               t.Errorf("PhysLocation must be Updater")
+       }
+       if _, ok := i.(api.Deleter); !ok {
+               t.Errorf("PhysLocation must be Deleter")
+       }
+       if _, ok := i.(api.Identifier); !ok {
+               t.Errorf("PhysLocation must be Identifier")
+       }
+}
+
+func TestValidate(t *testing.T) {
+       p := TOPhysLocation{}
+       errs := test.SortErrors(p.Validate(nil))
+       expected := test.SortErrors([]error{
+               errors.New("'state' cannot be blank"),
+               errors.New("'zip' cannot be blank"),
+               errors.New("'address' cannot be blank"),
+               errors.New("'city' cannot be blank"),
+               errors.New("'name' cannot be blank"),
+               errors.New("'regionId' cannot be blank"),
+               errors.New("'shortName' cannot be blank"),
+       })
+
+       if !reflect.DeepEqual(expected, errs) {
+               t.Errorf("expected %++v,  got %++v", expected, errs)
+       }
+}

-- 
To stop receiving notification emails like this one, please contact
dang...@apache.org.

Reply via email to