ocket8888 commented on code in PR #6817:
URL: https://github.com/apache/trafficcontrol/pull/6817#discussion_r869458440


##########
cache-config/testing/ort-tests/tcdata/statuses.go:
##########
@@ -19,6 +19,14 @@ import (
        "testing"
 )
 
+var statusNameMap = map[string]bool{

Review Comment:
   I see this map repeated in 5 different places in this PR. I think the reason 
you're doing that is because it's mutable so exposing it as an exported member 
of `lib/go-tc` could be problematic. But functions are immutable, so you could 
just have one function in the lib that does this instead of repeating it 5 
times:
   ```go
   // IsReservedStatus checks if the given status name is reserved; i.e. may 
not be
   // deleted or modified.
   func IsReservedStatus(status string) bool {
        switch (status) {
        case CacheStatusAdminDown:
                fallthrough
        case CacheStatusOffline:
                fallthrough
        case CacheStatusOnline:
                fallthrough
        case CacheStatusPreProd:
                fallthrough
        case CacheStatusReported:
                return true
        }
        return false
   }
   ```



##########
cache-config/testing/ort-tests/tcdata/statuses.go:
##########
@@ -46,17 +54,43 @@ func (r *TCData) DeleteTestStatuses(t *testing.T) {
                respStatus := resp[0]
 
                delResp, _, err := TOSession.DeleteStatusByID(respStatus.ID)
-               if err != nil {
-                       t.Errorf("cannot DELETE Status by name: %v - %v", err, 
delResp)
-               }
+               if _, ok := statusNameMap[*status.Name]; !ok {
+                       if err != nil {
+                               t.Errorf("cannot DELETE Status by name: %v - 
%v", err, delResp)
+                       }
 
-               // Retrieve the Status to see if it got deleted
-               types, _, err := TOSession.GetStatusByName(*status.Name)
-               if err != nil {
-                       t.Errorf("error deleting Status name: %v", err)
-               }
-               if len(types) > 0 {
-                       t.Errorf("expected Status name: %s to be deleted", 
*status.Name)
+                       // Retrieve the Status to see if it got deleted
+                       types, _, err := TOSession.GetStatusByName(*status.Name)
+                       if err != nil {
+                               t.Errorf("error deleting Status name: %v", err)
+                       }
+                       if len(types) > 0 {
+                               t.Errorf("expected Status name: %s to be 
deleted", *status.Name)
+                       }
+               } else {
+                       if err == nil {
+                               t.Errorf("expected an error while trying to 
delete a reserved status, but got nothing")
+                       }
                }
        }
+       r.ForceDeleteStatuses(t)
+}
+
+// ForceDeleteStatuses forcibly deletes the statuses from the db.
+func (r *TCData) ForceDeleteStatuses(t *testing.T) {

Review Comment:
   Instead of doing this, we should just treat the statuses as immutable, which 
they now are, and not create an invalid Traffic Ops instance by deleting them. 
That means removing the Statuses which already exist in every valid Traffic Ops 
instance from the testing data "fixtures".



##########
traffic_ops/app/db/migrations/2022050916074300_add_reserved_statuses.down.sql:
##########
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+DELETE FROM public.status WHERE name IN ('ONLINE', 'OFFLINE', 'REPORTED', 
'ADMIN_DOWN', 'PRE_PROD');

Review Comment:
   This will break existing TO installations. If you have data unchanged from 
seeds.sql and then try to upgrade with this migration, then downgrade back, 
first of all it will probably fail to roll back because there will almost 
certainly be servers using at least one of these statuses, but then also your 
data will not be left in the same state as it was pre-upgrade.
   
   This file should be blank. In the worst case scenario, that would mean 
downgrading creates statuses that need to exist for ATC to work, so there's 
only benefit.



##########
traffic_ops/testing/api/v2/statuses_test.go:
##########
@@ -42,35 +50,44 @@ func CreateTestStatuses(t *testing.T) {
 
 func UpdateTestStatuses(t *testing.T) {
 
-       firstStatus := testData.Statuses[0]
-       if firstStatus.Name == nil {
-               t.Fatal("cannot update test statuses: first test data status 
must have a name")
-       }
-
-       // Retrieve the Status by name so we can get the id for the Update
-       resp, _, err := TOSession.GetStatusByName(*firstStatus.Name)
-       if err != nil {
-               t.Errorf("cannot GET Status by name: %v - %v", 
firstStatus.Name, err)
-       }
-       remoteStatus := resp[0]
-       expectedStatusDesc := "new description"
-       remoteStatus.Description = expectedStatusDesc
-       var alert tc.Alerts
-       alert, _, err = TOSession.UpdateStatusByID(remoteStatus.ID, 
remoteStatus)
-       if err != nil {
-               t.Errorf("cannot UPDATE Status by id: %v - %v", err, alert)
-       }
-
-       // Retrieve the Status to check Status name got updated
-       resp, _, err = TOSession.GetStatusByID(remoteStatus.ID)
-       if err != nil {
-               t.Errorf("cannot GET Status by ID: %v - %v", 
firstStatus.Description, err)
+       if len(testData.Statuses) < 1 {
+               t.Fatal("Need at least one Status to test updating a Status")
        }
-       respStatus := resp[0]
-       if respStatus.Description != expectedStatusDesc {
-               t.Errorf("results do not match actual: %s, expected: %s", 
respStatus.Name, expectedStatusDesc)
+       for _, status := range testData.Statuses {
+               if status.Name == nil {
+                       t.Fatal("cannot update test statuses: test data status 
must have a name")
+               }
+               // Retrieve the Status by name so we can get the id for the 
Update
+               resp, _, err := TOSession.GetStatusByName(*status.Name)
+               if err != nil {
+                       t.Errorf("cannot GET Status by name: %v - %v", 
status.Name, err)
+               }
+               remoteStatus := resp[0]
+               expectedStatusDesc := "new description"
+               remoteStatus.Description = expectedStatusDesc
+               var alert tc.Alerts
+               alert, _, err = TOSession.UpdateStatusByID(remoteStatus.ID, 
remoteStatus)
+
+               if _, ok := statusNameMap[*status.Name]; ok {
+                       if err == nil {
+                               t.Errorf("expected an error about while 
updating a reserved status, but got nothing")
+                       }
+               } else {
+                       if err != nil {
+                               t.Errorf("cannot UPDATE Status by id: %v - %v", 
err, alert)
+                       }
+
+                       // Retrieve the Status to check Status name got updated
+                       resp, _, err = TOSession.GetStatusByID(remoteStatus.ID)
+                       if err != nil {
+                               t.Errorf("cannot GET Status by ID: %v - %v", 
status.Description, err)

Review Comment:
   Same as above; `status.Description` is a `*string`.



##########
cache-config/testing/ort-tests/tcdata/statuses.go:
##########
@@ -46,17 +54,43 @@ func (r *TCData) DeleteTestStatuses(t *testing.T) {
                respStatus := resp[0]
 
                delResp, _, err := TOSession.DeleteStatusByID(respStatus.ID)
-               if err != nil {
-                       t.Errorf("cannot DELETE Status by name: %v - %v", err, 
delResp)
-               }
+               if _, ok := statusNameMap[*status.Name]; !ok {
+                       if err != nil {
+                               t.Errorf("cannot DELETE Status by name: %v - 
%v", err, delResp)
+                       }
 
-               // Retrieve the Status to see if it got deleted
-               types, _, err := TOSession.GetStatusByName(*status.Name)
-               if err != nil {
-                       t.Errorf("error deleting Status name: %v", err)
-               }
-               if len(types) > 0 {
-                       t.Errorf("expected Status name: %s to be deleted", 
*status.Name)
+                       // Retrieve the Status to see if it got deleted
+                       types, _, err := TOSession.GetStatusByName(*status.Name)
+                       if err != nil {
+                               t.Errorf("error deleting Status name: %v", err)
+                       }
+                       if len(types) > 0 {
+                               t.Errorf("expected Status name: %s to be 
deleted", *status.Name)
+                       }
+               } else {
+                       if err == nil {
+                               t.Errorf("expected an error while trying to 
delete a reserved status, but got nothing")
+                       }
                }

Review Comment:
   nit:
   ```go
   ...
   } else {
       if condition {
           ...
       }
   }
   ```
   is equivalent to the shorter
   ```go
   ...
   } else if condition {
       ...
   }



##########
traffic_ops/traffic_ops_golang/status/statuses.go:
##########
@@ -119,9 +127,29 @@ func (st *TOStatus) Read(h http.Header, useIMS bool) 
([]interface{}, error, erro
        return readVals, nil, nil, errCode, maxTime
 }
 
-func (st *TOStatus) Update(h http.Header) (error, error, int) { return 
api.GenericUpdate(h, st) }
-func (st *TOStatus) Create() (error, error, int)              { return 
api.GenericCreate(st) }
-func (st *TOStatus) Delete() (error, error, int)              { return 
api.GenericDelete(st) }
+func (st *TOStatus) Update(h http.Header) (error, error, int) {
+       var statusName string
+       err := st.APIInfo().Tx.QueryRow(`SELECT name from status WHERE id = 
$1`, *st.ID).Scan(&statusName)
+       if err != nil {
+               return nil, fmt.Errorf("error querying status name from ID: 
%v", err), http.StatusInternalServerError

Review Comment:
   errors constructed from errors should wrap with `%w`.



##########
traffic_ops/testing/api/v2/statuses_test.go:
##########
@@ -42,35 +50,44 @@ func CreateTestStatuses(t *testing.T) {
 
 func UpdateTestStatuses(t *testing.T) {
 
-       firstStatus := testData.Statuses[0]
-       if firstStatus.Name == nil {
-               t.Fatal("cannot update test statuses: first test data status 
must have a name")
-       }
-
-       // Retrieve the Status by name so we can get the id for the Update
-       resp, _, err := TOSession.GetStatusByName(*firstStatus.Name)
-       if err != nil {
-               t.Errorf("cannot GET Status by name: %v - %v", 
firstStatus.Name, err)
-       }
-       remoteStatus := resp[0]
-       expectedStatusDesc := "new description"
-       remoteStatus.Description = expectedStatusDesc
-       var alert tc.Alerts
-       alert, _, err = TOSession.UpdateStatusByID(remoteStatus.ID, 
remoteStatus)
-       if err != nil {
-               t.Errorf("cannot UPDATE Status by id: %v - %v", err, alert)
-       }
-
-       // Retrieve the Status to check Status name got updated
-       resp, _, err = TOSession.GetStatusByID(remoteStatus.ID)
-       if err != nil {
-               t.Errorf("cannot GET Status by ID: %v - %v", 
firstStatus.Description, err)
+       if len(testData.Statuses) < 1 {
+               t.Fatal("Need at least one Status to test updating a Status")
        }
-       respStatus := resp[0]
-       if respStatus.Description != expectedStatusDesc {
-               t.Errorf("results do not match actual: %s, expected: %s", 
respStatus.Name, expectedStatusDesc)
+       for _, status := range testData.Statuses {
+               if status.Name == nil {
+                       t.Fatal("cannot update test statuses: test data status 
must have a name")
+               }
+               // Retrieve the Status by name so we can get the id for the 
Update
+               resp, _, err := TOSession.GetStatusByName(*status.Name)
+               if err != nil {
+                       t.Errorf("cannot GET Status by name: %v - %v", 
status.Name, err)

Review Comment:
   `status.Name` is a pointer to a string, so using `%v` to format it is going 
to not be very helpful since [`%v` formats `*string`s as memory 
addresses](https://go.dev/play/p/YEGLp3CQe7x). This is why types should always 
use the most specific formatting parameter they can; if you use `%s` here it 
will fail `go vet` and thus the test won't build, which lets you know 
immediately that it's not printing what it should be.



##########
traffic_ops/testing/api/v2/statuses_test.go:
##########
@@ -106,17 +123,43 @@ func DeleteTestStatuses(t *testing.T) {
                respStatus := resp[0]
 
                delResp, _, err := TOSession.DeleteStatusByID(respStatus.ID)
-               if err != nil {
-                       t.Errorf("cannot DELETE Status by name: %v - %v", err, 
delResp)
+               if _, ok := statusNameMap[*status.Name]; !ok {
+                       if err != nil {
+                               t.Errorf("cannot DELETE Status by name: %v - 
%v", err, delResp)
+                       }
+
+                       // Retrieve the Status to see if it got deleted
+                       types, _, err := TOSession.GetStatusByName(*status.Name)
+                       if err != nil {
+                               t.Errorf("error deleting Status name: %s", 
err.Error())

Review Comment:
   nit: `.Error()` is redundant because `%s` formats errors as strings by 
calling their `Error` method.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to