Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .gds/bundle.lock.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ bundle:
version: "0.4.0-dev"
release_sequence: 0
channel: "development"
source_tree_digest: "sha256:a18f15d00b467f9585fecf7ee3230f00025f69726e17135f14eb99f3036b5827"
digest: "sha256:7d370fff3455208269852d3bd0f7e4eff1e61b3eb8e80df9c967245bf464cda5"
source_tree_digest: "sha256:66532e92cee2ba8351911491ea4d8267fe853db10ebe36559551cb21aad5f209"
digest: "sha256:02032875c4b1e479d953ba4e9b766cf260bc469a81c38856ab6a6c6044b1c147"

projection:
input_digest: "sha256:88f5073a543617f2b37990db826b8777ef8c396eb658de403f60e2e480e15f62"
output_digest: "sha256:d76ab0b017214dce592c2ae8b23e30b89caabf5a0197be3b25dae6d948cd00aa"
input_digest: "sha256:02a7ff040caee7de0f5de1b2402485a7e6972b68e867413ad7a1d71add5d0336"
output_digest: "sha256:8fd7fc9e1adc79eec4876daaa1d9711882d3959584c92fc5c427c64ee6ecebce"
files:
- path: ".gds/compiled-policy.json"
digest: "sha256:78d09606bb4168d74bce1f50ab62b46a7ded34652c6b23af1badfd26dd060e94"
- path: ".github/workflows/gds-ci.yml"
digest: "sha256:1508f13759d4a238114e35d7c6ee6e0f6d2b3b09e18d8fb7eff46b534143cb73"
digest: "sha256:184bc5f09a65158b07fe4d6948c4d805869ddb35f9dcfaf528c5f5b4720abebb"
4 changes: 2 additions & 2 deletions .github/workflows/gds-ci.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# GENERATED FILE - DO NOT EDIT DIRECTLY
# generator: gds
# bundle: 0.4.0-dev
# source-tree-digest: sha256:a18f15d00b467f9585fecf7ee3230f00025f69726e17135f14eb99f3036b5827
# input-digest: sha256:88f5073a543617f2b37990db826b8777ef8c396eb658de403f60e2e480e15f62
# source-tree-digest: sha256:66532e92cee2ba8351911491ea4d8267fe853db10ebe36559551cb21aad5f209
# input-digest: sha256:02a7ff040caee7de0f5de1b2402485a7e6972b68e867413ad7a1d71add5d0336
# output-digest: sha256:01fb4854784be9e4564bcc84e70786484b370879be5e5ab1dd49f8b73ea2dea4
# edit-source:
# - .gds/repository.yaml
Expand Down
17 changes: 17 additions & 0 deletions core/app/module_coverage.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,23 @@ func (services *Services) CoverModules(
Evidence: map[string]any{"module": selected},
})
}
// Coverage over nothing is not coverage. Without this the command reports
// success having compared no module against any gate, which is
// indistinguishable from every module being covered -- and the estates most
// likely to hit it are the ones whose repositories were never declared as
// modules, which is exactly when someone is asking whether their gates are
// watched.
if len(data.Modules) == 0 {
findings = append(findings, domain.Finding{
Code: "GDS_MODULE_COVERAGE_SCOPE_NOT_PROVEN", Severity: domain.SeverityHigh,
Message: "No declared git-submodule-consumer relationship was covered, " +
"so no gate was compared.",
Evidence: map[string]any{
"declared_relationships": len(consumer.Relationships),
"repository_id": consumer.Repository.ID,
},
})
}
return domain.NewEnvelope(command, classifyFindings(findings), data, findings...)
}

Expand Down
44 changes: 44 additions & 0 deletions core/app/module_coverage_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package app

import (
"context"
"testing"

"github.com/NDDev-OpenNetwork/github-device-sync/core/domain"
)

// TestModuleCoverageOverNothingIsNotProven covers the case the command was
// silent about. An estate whose repositories are not declared as
// git-submodule-consumer relationships gets an empty module set, and reporting
// success there is indistinguishable from every module being covered -- while
// the estates most likely to hit it are exactly the ones asking whether their
// gates are watched at all.
//
// This runs against the external estate fixture rather than the developer's own
// machine. A first version of this test lived in core/cli and passed locally
// only because a registered estate happened to be configured there; in CI the
// command failed earlier at GDS_POLICY_ESTATE_NOT_PROVEN and never reached this
// path, so it was green over the wrong refusal.
func TestModuleCoverageOverNothingIsNotProven(t *testing.T) {
root := appTestRepositoryRoot(t)
runtimePath := appTestRuntimeConfig(t, root)
services, err := NewServices(DefaultClock)
if err != nil {
t.Fatal(err)
}
envelope := services.CoverModules(context.Background(), root, ModuleCoverageOptions{
GitHubReadOptions: GitHubReadOptions{RuntimeConfig: runtimePath},
})
if envelope.ExitClass == domain.ExitSuccess {
t.Fatalf("coverage over an empty module set reported success: %#v", envelope)
}
found := false
for _, finding := range envelope.Findings {
if finding.Code == "GDS_MODULE_COVERAGE_SCOPE_NOT_PROVEN" {
found = true
}
}
if !found {
t.Fatalf("no GDS_MODULE_COVERAGE_SCOPE_NOT_PROVEN finding: %#v", envelope)
}
}