diff --git a/pkg/analysis/passes/coderules/coderules_test.go b/pkg/analysis/passes/coderules/coderules_test.go index 6c91aca0..863d51fa 100644 --- a/pkg/analysis/passes/coderules/coderules_test.go +++ b/pkg/analysis/passes/coderules/coderules_test.go @@ -728,3 +728,52 @@ func TestNoAPIDsQueryEndpointGood(t *testing.T) { require.NoError(t, err) require.Len(t, interceptor.Diagnostics, 0) } + +func TestBackendDatasourceShouldNotImplementTestDatasource(t *testing.T) { + if !isSemgrepInstalled() { + t.Skip("semgrep not installed, skipping test") + return + } + var interceptor testpassinterceptor.TestPassInterceptor + pass := &analysis.Pass{ + RootDir: filepath.Join("./"), + ResultOf: map[*analysis.Analyzer]interface{}{ + sourcecode.Analyzer: filepath.Join("testdata", "backend-datasource-testdatasource-bad"), + }, + Report: interceptor.ReportInterceptor(), + } + + _, err := Analyzer.Run(pass) + require.NoError(t, err) + require.Len(t, interceptor.Diagnostics, 1) + require.Equal(t, analysis.Error, interceptor.Diagnostics[0].Severity) + require.Equal( + t, + "Backend datasources (classes extending DataSourceWithBackend) must not implement testDatasource(). Remove this method and rely on backend health checks.", + interceptor.Diagnostics[0].Title, + ) + require.Equal( + t, + "code-rules-backend-datasource-should-not-implement-testdatasource", + interceptor.Diagnostics[0].Name, + ) +} + +func TestBackendDatasourceWithoutTestDatasourceGood(t *testing.T) { + if !isSemgrepInstalled() { + t.Skip("semgrep not installed, skipping test") + return + } + var interceptor testpassinterceptor.TestPassInterceptor + pass := &analysis.Pass{ + RootDir: filepath.Join("./"), + ResultOf: map[*analysis.Analyzer]interface{}{ + sourcecode.Analyzer: filepath.Join("testdata", "backend-datasource-testdatasource-good"), + }, + Report: interceptor.ReportInterceptor(), + } + + _, err := Analyzer.Run(pass) + require.NoError(t, err) + require.Len(t, interceptor.Diagnostics, 0) +} diff --git a/pkg/analysis/passes/coderules/semgrep-rules.yaml b/pkg/analysis/passes/coderules/semgrep-rules.yaml index d3d1861a..86316805 100644 --- a/pkg/analysis/passes/coderules/semgrep-rules.yaml +++ b/pkg/analysis/passes/coderules/semgrep-rules.yaml @@ -353,3 +353,12 @@ rules: message: "Direct frontend usage of '/api/ds/query' is not permitted. Use Grafana runtime APIs getDataSourceSrv().get(...) and then query(...) instead of calling this endpoint directly." languages: [javascript, typescript] severity: ERROR + + - id: backend-datasource-should-not-implement-testdatasource + pattern-regex: (?s)class\s+\w+\s+extends\s+DataSourceWithBackend(?:<[^>]+>)?\s*\{.*?\btestDatasource\s*\( + paths: + include: + - "**/datasource.ts" + message: "Backend datasources (classes extending DataSourceWithBackend) must not implement testDatasource(). Remove this method and rely on backend health checks." + languages: [typescript] + severity: ERROR diff --git a/pkg/analysis/passes/coderules/testdata/backend-datasource-testdatasource-bad/src/datasource.ts b/pkg/analysis/passes/coderules/testdata/backend-datasource-testdatasource-bad/src/datasource.ts new file mode 100644 index 00000000..4acc3957 --- /dev/null +++ b/pkg/analysis/passes/coderules/testdata/backend-datasource-testdatasource-bad/src/datasource.ts @@ -0,0 +1,10 @@ +import { DataSourceWithBackend } from '@grafana/runtime'; + +export class DataSource extends DataSourceWithBackend { + async testDatasource() { + return { + status: 'success', + message: 'ok', + }; + } +} diff --git a/pkg/analysis/passes/coderules/testdata/backend-datasource-testdatasource-good/src/datasource.ts b/pkg/analysis/passes/coderules/testdata/backend-datasource-testdatasource-good/src/datasource.ts new file mode 100644 index 00000000..f546883f --- /dev/null +++ b/pkg/analysis/passes/coderules/testdata/backend-datasource-testdatasource-good/src/datasource.ts @@ -0,0 +1,3 @@ +import { DataSourceWithBackend } from '@grafana/runtime'; + +export class DataSource extends DataSourceWithBackend {}