Conversation
14b33db to
968745a
Compare
|
Right now the way these test helper functions are written there is no easy way to write test that verify an error condition. Right now the test fails, right away because we pass test object |
968745a to
35fd587
Compare
35fd587 to
1e5d6e3
Compare
9f06d73 to
d7e4f66
Compare
|
@invidian PTAL all the comments have been addressed. |
d7e4f66 to
56f55b1
Compare
| // what error to expect. | ||
| func JSONPathExists(t *testing.T, yamlConfig string, jsonPath string, errExp string) { | ||
| _, err := jsonPathValue(yamlConfig, jsonPath) | ||
| if err != nil && errExp == "" { //nolint:gocritic |
There was a problem hiding this comment.
I would avoid disabling gocritic.
There was a problem hiding this comment.
It is suggesting usage of a switch case instead of the if else. Given the kinda checks we have switch case is not ideal.
There was a problem hiding this comment.
It also uses elses a lot, which are discouraged in Go..
There was a problem hiding this comment.
I think we can just remove the else's here.
There was a problem hiding this comment.
We could also use a const error here, then complexity of the whole thing is reduced to just errors.Is() call, which I think is elegant. I can share the code for that.
There was a problem hiding this comment.
Removed else because they are each func is just a way to exit out of the function.
There was a problem hiding this comment.
How is errors.Is() or any const we create going to help with the error coming from upstream lib? Especially when they have not declared an error var for us to consume but just an error created using fmt.Errorf.
There was a problem hiding this comment.
How is errors.Is() or any const we create going to help with the error coming from upstream lib? Especially when they have not declared an error var for us to consume but just an error created using fmt.Errorf.
If we set allowMissingKeys for jsonpath, then we get no value, so we can return constant ValueNotFound error.
Though I'm not a big fan of this const error pattern, as it adds bunch of boilerplate. It would look something like:
type NotFoundError string
func (e NotFoundError) Error() string {
return string(e)
}
const ErrNotFound NotFoundError = "not found"
type KeyNotFoundError struct {
Key string
}
func (e KeyNotFoundError) Error() string {
return fmt.Sprintf("%s %s", e.Key, ErrNotFound)
}
func (e KeyNotFoundError) Unwrap() error {
return ErrNotFound
}
func jsonPathValue(data, key string) (string, error) {
if !strings.Contains(data, key) {
return "", KeyNotFoundError{Key: key}
}
return "", nil
}
func JSONPathExistsSimplified(t *testing.T, yamlConfig string, jsonPath string, errExp error) {
_, err := jsonPathValue(yamlConfig, jsonPath)
if !errors.Is(err, errExp) {
t.Fatalf("Unexpected error: %v, expected: %v", err, errExp)
}
}So I think returning nil value would probably also work fine.
|
|
||
| if len(v) == 0 || len(v[0]) == 0 { | ||
| t.Fatalf("No result found") | ||
| return reflect.Value{}, fmt.Errorf("no result found") |
There was a problem hiding this comment.
Rather than returning a error here, how about we change the return type to *reflect.Value and we return nil, nil.
This way, we know everything went well, including query parsing, but the value was simply not found. This will make checking more idiomatic.
There was a problem hiding this comment.
It is reworked now. The functionality is not changed just returning an error to bubble up the caller stack.
There was a problem hiding this comment.
But we still must use strings.Contains, which IMO should be used as a last resort tool, as it test specific implementation.
With approach I suggested, we avoid ambiguity and we work against the specification of function.
There was a problem hiding this comment.
I agree *reflect.Value makes sense to distinguish errors from value not found.
There was a problem hiding this comment.
The newly added test helper JSONPathExists is currently relying on the error from this function call:
v, err := jPath.FindResults(obj.Object)So I don't understand how is the change to the pointer going to help in anything? We will still have to check for the error and match the string.
The error is not coming from this block:
if len(v) == 0 || len(v[0]) == 0 {
return reflect.Value{}, fmt.Errorf("no result found")
}Also jPath.FindResults returns [][]reflect.Value which definitely not a array of pointers so we don't need to worry about deep copying it if we convert it to pointer.
There was a problem hiding this comment.
@surajssd I think calling jPath.AllowMissingKeys() should help then?
There was a problem hiding this comment.
What's wrong with getting an error on something that does not exist?
It is possible that user (in this case a developer) might be giving wrong jsonpath and expecting something to exist. Such cases are ignored with AllowMissingKeys or gives wrong results.
There was a problem hiding this comment.
developer would expect certain value, so they must check for nil values. So either we have const error or return nil, nil, both have similar result with slightly different interface. IMO const error is cleaner from API point of view, but it brings more boilerplate for maintenance. I'm not sure which one is better, other than returning nil is simpler.
There was a problem hiding this comment.
AllowMissingKeys gives us strong gaurantee on which we rely on, so not making at false. But yes returning nil instead of error, then it is up to the user to figure out if there is an empty result.
56f55b1 to
e7e25d3
Compare
invidian
left a comment
There was a problem hiding this comment.
I still think k8sutil: Rename YAML imports should be split and rebased.
| // what error to expect. | ||
| func JSONPathExists(t *testing.T, yamlConfig string, jsonPath string, errExp string) { | ||
| _, err := jsonPathValue(yamlConfig, jsonPath) | ||
| if err != nil && errExp == "" { //nolint:gocritic |
There was a problem hiding this comment.
It also uses elses a lot, which are discouraged in Go..
|
|
||
| if len(v) == 0 || len(v[0]) == 0 { | ||
| t.Fatalf("No result found") | ||
| return reflect.Value{}, fmt.Errorf("no result found") |
There was a problem hiding this comment.
But we still must use strings.Contains, which IMO should be used as a last resort tool, as it test specific implementation.
With approach I suggested, we avoid ambiguity and we work against the specification of function.
|
|
||
| if len(v) == 0 || len(v[0]) == 0 { | ||
| t.Fatalf("No result found") | ||
| return reflect.Value{}, fmt.Errorf("no result found") |
There was a problem hiding this comment.
I agree *reflect.Value makes sense to distinguish errors from value not found.
| // what error to expect. | ||
| func JSONPathExists(t *testing.T, yamlConfig string, jsonPath string, errExp string) { | ||
| _, err := jsonPathValue(yamlConfig, jsonPath) | ||
| if err != nil && errExp == "" { //nolint:gocritic |
There was a problem hiding this comment.
I think we can just remove the else's here.
77e998e to
01ea884
Compare
01ea884 to
0317077
Compare
968085c to
9c206e1
Compare
iaguis
left a comment
There was a problem hiding this comment.
A couple of nits. Looks good to me in general.
This (`k8s.io/apimachinery/pkg/util/yaml`) is renamed to `yamlutil` so that in the next commit when we import `sigs.k8s.io/yaml` it can simply be used as `yaml.`. Signed-off-by: Suraj Deshmukh <suraj@kinvolk.io>
This commit adds an object ObjectMetadata to identify any Kubernetes object uniquely. Adds three exported functions. YAMLToUnstructured converts any YAML object to `unstructured.Unstructured` type. SplitYAMLDocuments splits a YAML file with multiple YAML documents separated by `---` into separate YAML configs. YAMLToObjectMetadata takes in a YAML config as string and returns ObjectMetadata. Add unit tests for each of the exported functions. Signed-off-by: Suraj Deshmukh <suraj@kinvolk.io>
This commit removes the local function `unstructredObj` and uses the function from the `k8sutil` package. Signed-off-by: Suraj Deshmukh <suraj@kinvolk.io>
This commit modifies the behaviour of passing the test object of type `testing.T` to the helper functions, instead return the errors. Signed-off-by: Suraj Deshmukh <suraj@kinvolk.io>
Earlier the conversion tests were using unreliable file name generated by Helm to find a Kubernetes object's YAML. Now this commit changes that and the users should now provide Version, Kind and Name to find the specific object. This specially solves the problem when a single file has multiple YAML documents separated by `---`, the new functions provide reliable way of finding the objects. Change the signature of ConfigFromMap to accept `k8sutil.ObjectMetadata` instead of string indicating file name. Modify the conversion tests to provide `k8sutil.ObjectMetadata` to find specific object. Signed-off-by: Suraj Deshmukh <suraj@kinvolk.io>
This change was introduced in `8d44c8513f67512527218c8a538e79a61cb7cd34` - "don't expose Envoy using hostPorts". But a later commit overrode it: `6a6a3c5d849b981559c2cc2cf3797bfd6b6c9159` - "contour: Update to v1.7.0". This commit fixes this regression. Add test to verify if hostPort does not exist, check if the converted config does not contain the hostPort. This commit also adds test to check if a certain JSONPath exists, it is used in the above unit test. Signed-off-by: Suraj Deshmukh <suraj@kinvolk.io>
9c206e1 to
ce837ac
Compare
|
AWS CI fail seems legit |
This change was introduced in 8d44c85 - "don't expose Envoy using hostPorts". But a later commit overrode it: 6a6a3c5 - "contour: Update to v1.7.0". This commit fixes this regression.
Fixes: #1336