Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setup test fixtures for resolvers #5317

Merged
merged 34 commits into from
Sep 19, 2024
Merged

Setup test fixtures for resolvers #5317

merged 34 commits into from
Sep 19, 2024

Conversation

egor-ryashin
Copy link
Contributor

No description provided.

Copy link
Contributor

@begelundmuller begelundmuller left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some other comments:

  1. Put .yaml and .csv files into a separate runtime/resolvers/testdata directory
  2. Add support for ClickHouse and Druid tests
  3. Do you think it makes sense to have some kind of --update flag that will write out resolver results to the test files? To make it easier to author new tests. E.g. see this for an example: https://ieftimov.com/posts/testing-in-go-golden-files/

runtime/resolvers/resolvers_test.go Outdated Show resolved Hide resolved
Comment on lines 24 to 29
type SecurityClaims struct {
// UserAttributes about the current user (or service account). Usually exposed through templating as {{ .user }}.
UserAttributes map[string]any
UserAttributes map[string]any `yaml:"user_attributes"`
// AdditionalRules are optional security rules to apply *in addition* to the built-in rules and the rules defined on the requested resource.
// These are currently leveraged by the admin service to enforce restrictions for magic auth tokens.
AdditionalRules []*runtimev1.SecurityRule
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we remove the yaml parsing from this type (can be handled with a separate type in the tests)? It makes it look like this type can be serialized/deserialized to YAML, which it can't (because the proto type runtimev1.SecurityRule is not YAML compatible)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder why we cannot do it YAML compatible, more copies of structs - more structs to maintain - harder to maintain when 2 structs are almost equal (finding the distinction is harder) - more error-prone.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As written above, the *runtimev1.SecurityRule proto-generated type is not safe for use with YAML, so we don't want to give the impression that the SecurityClaims type is YAML-serializable. Additionally, it's just a bad practice to hack an internal type that used in many places for a single specific use case (in this case, test fixtures) – it creates unnecessary coupling.

In this case, UserAttributes is the only field we need to be able to populate from the tests, so it is simple to parse it from the test fixture and create a SecurityClaims value for it.

Comment on lines 20 to 25
type Project struct {
Sources map[string]yaml.Node
Models map[string]yaml.Node
Dashboards map[string]yaml.Node
APIs map[string]yaml.Node
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of hard-coding the types, can we just use full paths as key types? We support quite a lot more resource types than just these four now. See this comment:
#5218 (comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally don't see much difference:

sources:
  1.yaml
  2.yaml
dashboards:
  1.yaml
  2.yaml
- sources/1.yaml:
- sources/2.yaml:
- dashboards/1.yaml:
- dashboards/2.yaml:

We can easily add a new field to Project struct if more resources is required.
That could make sense if you'd like to process all files equally with a single for-loop and it can save a few lines of code but you'll have to repeat yourself if you need multiple similar-type entries in the future, ie:

- sources/1.yaml:
- sources/2.yaml:
- dashboards/1.yaml:
- dashboards/2.yaml:

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO reducing nesting one level for all the test files definitely improves readability here. The very slight repetition of the directory name does not hurt readabilty IMO, and anyway we don't require specific directory names anymore, so the files can just be created at the root level.

This also has the additional benefit of simplifying the test fixture parsing code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

runtime/resolvers/resolvers_test.go Outdated Show resolved Hide resolved
runtime/resolvers/resolvers_test.go Outdated Show resolved Hide resolved
Comment on lines 85 to 87
options:
claims:
user_attributes:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the sake of cleaner tests, I think we should flatten the nesting here and just have user_attributes as a root property of the test type

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It relates to the previous argument If you like to have more test structures then it's less production code coverage.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand that we would like to see a more concise schema but it's already have a diverse structure, simplifying it is short term goal - eventually we will need to test more production fields anyway.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't impact production code coverage at all – it just makes the test fixture simpler to read/write. Internally the test fixture parser would create a SecurityClaims value, so the production code coverage is exactly the same.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@nishantmonu51
Copy link
Collaborator

@egor-ryashin : Any updates on this PR ?

@egor-ryashin
Copy link
Contributor Author

@nishantmonu51 Most of comments I'll resolve in 1d, the first one "updates" can take longer.

@egor-ryashin
Copy link
Contributor Author

  1. Put .yaml and .csv files into a separate runtime/resolvers/testdata directory
  2. Add support for ClickHouse and Druid tests
  3. Do you think it makes sense to have some kind of --update flag that will write out resolver results to the test files? To make it easier to author new tests. E.g. see this for an example: https://ieftimov.com/posts/testing-in-go-golden-files/
  1. Done (replaced with the public https source - no need to commit data).
  2. Done.

@egor-ryashin
Copy link
Contributor Author

Merge conflicts resolved.

@egor-ryashin egor-ryashin marked this pull request as ready for review August 5, 2024 11:12
@egor-ryashin
Copy link
Contributor Author

Updates having this issue go-yaml/yaml#430
Floats are overwritten with integers breaking unit tests consequently, trying to workaround.

@egor-ryashin
Copy link
Contributor Author

egor-ryashin commented Aug 7, 2024

Updates is done.

go.mod Outdated
@@ -261,6 +261,7 @@ require (
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
github.com/maxatome/go-testdeep v1.14.0 // indirect
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indirect dependency added without change to a direct dependency – try running go mod tidy

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Comment on lines 69 to 71
if strings.Contains(err.Error(), c.dsn) { // avoid returning the actual DSN with the password which will be logged
return nil, fmt.Errorf("invalid dsn")
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps return the original error, but with something like strings.Replace(errMsg, c.dsn, "<masked>")?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -0,0 +1,91 @@
project:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move the .yaml test fixtures into a separate directory at runtime/resolvers/testdata since the Go docs recommend putting non-Go test files in a separate testdata directory

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment on lines 69 to 72
// logger := zap.NewNop()
// nolint
// logger, err := zap.NewDevelopment()
// require.NoError(t, err)
logger, err := zap.NewDevelopment()
require.NoError(t, err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert before merging – makes the CI test output difficult to read

Comment on lines 46 to 48
func TestResolvers(t *testing.T) {

entries, err := os.ReadDir("./")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: redundant newline

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

InstanceOptions
OLAPDriver string
OLAPDSN string
TempDir string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This appears unused

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

Comment on lines 24 to 29
type SecurityClaims struct {
// UserAttributes about the current user (or service account). Usually exposed through templating as {{ .user }}.
UserAttributes map[string]any
UserAttributes map[string]any `yaml:"user_attributes"`
// AdditionalRules are optional security rules to apply *in addition* to the built-in rules and the rules defined on the requested resource.
// These are currently leveraged by the admin service to enforce restrictions for magic auth tokens.
AdditionalRules []*runtimev1.SecurityRule
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As written above, the *runtimev1.SecurityRule proto-generated type is not safe for use with YAML, so we don't want to give the impression that the SecurityClaims type is YAML-serializable. Additionally, it's just a bad practice to hack an internal type that used in many places for a single specific use case (in this case, test fixtures) – it creates unnecessary coupling.

In this case, UserAttributes is the only field we need to be able to populate from the tests, so it is simple to parse it from the test fixture and create a SecurityClaims value for it.

Comment on lines 20 to 25
type Project struct {
Sources map[string]yaml.Node
Models map[string]yaml.Node
Dashboards map[string]yaml.Node
APIs map[string]yaml.Node
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO reducing nesting one level for all the test files definitely improves readability here. The very slight repetition of the directory name does not hurt readabilty IMO, and anyway we don't require specific directory names anymore, so the files can just be created at the root level.

This also has the additional benefit of simplifying the test fixture parsing code.

Comment on lines 85 to 87
options:
claims:
user_attributes:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't impact production code coverage at all – it just makes the test fixture simpler to read/write. Internally the test fixture parser would create a SecurityClaims value, so the production code coverage is exactly the same.

Comment on lines 27 to 37
type Resolvers struct {
Project Project
Connectors map[string]*testruntime.InstanceOptionsForResolvers
Tests map[string]*Test
}

type Test struct {
Resolver string
Options runtime.ResolveOptions
Result []map[string]any
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I strongly believe it would simplify the code and readability here to combine this into a single struct with anonymous sub-structs to represent the YAML structure – and avoid using internal types.

This is also what we do for the resource YAML parsing, and it serves us well. Look at the AlertYAML type here as an example:

type AlertYAML struct {

Copy link
Contributor Author

@egor-ryashin egor-ryashin Aug 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, but I'll need to copy all the data to ResolveOptions to call a method here:

					res, err := rt.Resolve(context.Background(), &ropts)

Copy link
Contributor Author

@egor-ryashin egor-ryashin Aug 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see SecurityClaims have a oneof field, now it makes sense.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@egor-ryashin
Copy link
Contributor Author

All done.

@begelundmuller
Copy link
Contributor

Can you take a look at the CI failures?

@egor-ryashin
Copy link
Contributor Author

There's a weird error currently

        	            	rill.runtime.v1.Model/ad_bids_mini: failed to create model: code: 27, message: Cannot parse input: expected ',' before: 'Z,,msn.com,2,1,2,cars,,iphone\n2,2022-01-03T14:49:50.459Z,,msn.com,2.5,10,1,cars,,iphone\n1,2022-01-02T11:58:12.475Z,Yahoo,yahoo.com,2,100,1,cars,1,\n3,2022-01-04T': (at row 1)

It's not reproduced locally with the same docker image.

@egor-ryashin
Copy link
Contributor Author

egor-ryashin commented Sep 3, 2024

There's a new exception emerged after the merge:

ORDER BY or PRIMARY KEY clause is missing. Consider using extended storage definition syntax with ORDER BY or PRIMARY KEY clause. With deprecated old syntax (highly not recommended) storage MergeTree requires 3 to 4 parameters: 
            	            	name of column with date,
            	            	[sampling element of primary key],
            	            	primary key expression,
            	            	index granularity
            	            	
            	            	Syntax for the MergeTree table engine:
            	            	
            	            	CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
            	            	(
            	            	    name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1] [TTL expr1],

I'm figuring that out.

@egor-ryashin
Copy link
Contributor Author

Solved here #5591

@egor-ryashin
Copy link
Contributor Author

All done.

@begelundmuller begelundmuller merged commit 370c29d into main Sep 19, 2024
7 checks passed
@begelundmuller begelundmuller deleted the resolvers-test-fixture branch September 19, 2024 11:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants