1/*
2Copyright 2014 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package errors
18
19import (
20	"errors"
21	"fmt"
22	"reflect"
23	"testing"
24
25	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26	"k8s.io/apimachinery/pkg/runtime"
27	"k8s.io/apimachinery/pkg/runtime/schema"
28	"k8s.io/apimachinery/pkg/util/validation/field"
29)
30
31func resource(resource string) schema.GroupResource {
32	return schema.GroupResource{Group: "", Resource: resource}
33}
34func kind(kind string) schema.GroupKind {
35	return schema.GroupKind{Group: "", Kind: kind}
36}
37
38func TestErrorNew(t *testing.T) {
39	err := NewAlreadyExists(resource("tests"), "1")
40	if !IsAlreadyExists(err) {
41		t.Errorf("expected to be %s", metav1.StatusReasonAlreadyExists)
42	}
43	if IsConflict(err) {
44		t.Errorf("expected to not be %s", metav1.StatusReasonConflict)
45	}
46	if IsNotFound(err) {
47		t.Errorf(fmt.Sprintf("expected to not be %s", metav1.StatusReasonNotFound))
48	}
49	if IsInvalid(err) {
50		t.Errorf("expected to not be %s", metav1.StatusReasonInvalid)
51	}
52	if IsBadRequest(err) {
53		t.Errorf("expected to not be %s", metav1.StatusReasonBadRequest)
54	}
55	if IsForbidden(err) {
56		t.Errorf("expected to not be %s", metav1.StatusReasonForbidden)
57	}
58	if IsServerTimeout(err) {
59		t.Errorf("expected to not be %s", metav1.StatusReasonServerTimeout)
60	}
61	if IsMethodNotSupported(err) {
62		t.Errorf("expected to not be %s", metav1.StatusReasonMethodNotAllowed)
63	}
64
65	if !IsConflict(NewConflict(resource("tests"), "2", errors.New("message"))) {
66		t.Errorf("expected to be conflict")
67	}
68	if !IsNotFound(NewNotFound(resource("tests"), "3")) {
69		t.Errorf("expected to be %s", metav1.StatusReasonNotFound)
70	}
71	if !IsInvalid(NewInvalid(kind("Test"), "2", nil)) {
72		t.Errorf("expected to be %s", metav1.StatusReasonInvalid)
73	}
74	if !IsBadRequest(NewBadRequest("reason")) {
75		t.Errorf("expected to be %s", metav1.StatusReasonBadRequest)
76	}
77	if !IsForbidden(NewForbidden(resource("tests"), "2", errors.New("reason"))) {
78		t.Errorf("expected to be %s", metav1.StatusReasonForbidden)
79	}
80	if !IsUnauthorized(NewUnauthorized("reason")) {
81		t.Errorf("expected to be %s", metav1.StatusReasonUnauthorized)
82	}
83	if !IsServerTimeout(NewServerTimeout(resource("tests"), "reason", 0)) {
84		t.Errorf("expected to be %s", metav1.StatusReasonServerTimeout)
85	}
86	if !IsMethodNotSupported(NewMethodNotSupported(resource("foos"), "delete")) {
87		t.Errorf("expected to be %s", metav1.StatusReasonMethodNotAllowed)
88	}
89
90	if time, ok := SuggestsClientDelay(NewServerTimeout(resource("tests"), "doing something", 10)); time != 10 || !ok {
91		t.Errorf("unexpected %d", time)
92	}
93	if time, ok := SuggestsClientDelay(NewServerTimeout(resource("tests"), "doing something", 0)); time != 0 || !ok {
94		t.Errorf("unexpected %d", time)
95	}
96	if time, ok := SuggestsClientDelay(NewTimeoutError("test reason", 10)); time != 10 || !ok {
97		t.Errorf("unexpected %d", time)
98	}
99	if time, ok := SuggestsClientDelay(NewTooManyRequests("doing something", 10)); time != 10 || !ok {
100		t.Errorf("unexpected %d", time)
101	}
102	if time, ok := SuggestsClientDelay(NewTooManyRequests("doing something", 1)); time != 1 || !ok {
103		t.Errorf("unexpected %d", time)
104	}
105	if time, ok := SuggestsClientDelay(NewGenericServerResponse(429, "get", resource("tests"), "test", "doing something", 10, true)); time != 10 || !ok {
106		t.Errorf("unexpected %d", time)
107	}
108	if time, ok := SuggestsClientDelay(NewGenericServerResponse(500, "get", resource("tests"), "test", "doing something", 10, true)); time != 10 || !ok {
109		t.Errorf("unexpected %d", time)
110	}
111	if time, ok := SuggestsClientDelay(NewGenericServerResponse(429, "get", resource("tests"), "test", "doing something", 0, true)); time != 0 || ok {
112		t.Errorf("unexpected %d", time)
113	}
114}
115
116func TestNewInvalid(t *testing.T) {
117	testCases := []struct {
118		Err     *field.Error
119		Details *metav1.StatusDetails
120	}{
121		{
122			field.Duplicate(field.NewPath("field[0].name"), "bar"),
123			&metav1.StatusDetails{
124				Kind: "Kind",
125				Name: "name",
126				Causes: []metav1.StatusCause{{
127					Type:  metav1.CauseTypeFieldValueDuplicate,
128					Field: "field[0].name",
129				}},
130			},
131		},
132		{
133			field.Invalid(field.NewPath("field[0].name"), "bar", "detail"),
134			&metav1.StatusDetails{
135				Kind: "Kind",
136				Name: "name",
137				Causes: []metav1.StatusCause{{
138					Type:  metav1.CauseTypeFieldValueInvalid,
139					Field: "field[0].name",
140				}},
141			},
142		},
143		{
144			field.NotFound(field.NewPath("field[0].name"), "bar"),
145			&metav1.StatusDetails{
146				Kind: "Kind",
147				Name: "name",
148				Causes: []metav1.StatusCause{{
149					Type:  metav1.CauseTypeFieldValueNotFound,
150					Field: "field[0].name",
151				}},
152			},
153		},
154		{
155			field.NotSupported(field.NewPath("field[0].name"), "bar", nil),
156			&metav1.StatusDetails{
157				Kind: "Kind",
158				Name: "name",
159				Causes: []metav1.StatusCause{{
160					Type:  metav1.CauseTypeFieldValueNotSupported,
161					Field: "field[0].name",
162				}},
163			},
164		},
165		{
166			field.Required(field.NewPath("field[0].name"), ""),
167			&metav1.StatusDetails{
168				Kind: "Kind",
169				Name: "name",
170				Causes: []metav1.StatusCause{{
171					Type:  metav1.CauseTypeFieldValueRequired,
172					Field: "field[0].name",
173				}},
174			},
175		},
176	}
177	for i, testCase := range testCases {
178		vErr, expected := testCase.Err, testCase.Details
179		expected.Causes[0].Message = vErr.ErrorBody()
180		err := NewInvalid(kind("Kind"), "name", field.ErrorList{vErr})
181		status := err.ErrStatus
182		if status.Code != 422 || status.Reason != metav1.StatusReasonInvalid {
183			t.Errorf("%d: unexpected status: %#v", i, status)
184		}
185		if !reflect.DeepEqual(expected, status.Details) {
186			t.Errorf("%d: expected %#v, got %#v", i, expected, status.Details)
187		}
188	}
189}
190
191func TestReasonForError(t *testing.T) {
192	if e, a := metav1.StatusReasonUnknown, ReasonForError(nil); e != a {
193		t.Errorf("unexpected reason type: %#v", a)
194	}
195}
196
197type TestType struct{}
198
199func (obj *TestType) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind }
200func (obj *TestType) DeepCopyObject() runtime.Object {
201	if obj == nil {
202		return nil
203	}
204	clone := *obj
205	return &clone
206}
207
208func TestFromObject(t *testing.T) {
209	table := []struct {
210		obj     runtime.Object
211		message string
212	}{
213		{&metav1.Status{Message: "foobar"}, "foobar"},
214		{&TestType{}, "unexpected object: &{}"},
215	}
216
217	for _, item := range table {
218		if e, a := item.message, FromObject(item.obj).Error(); e != a {
219			t.Errorf("Expected %v, got %v", e, a)
220		}
221	}
222}
223