diff --git a/.changeset/fix_hosts_query_properly_take_into_account_the_field_usability_low_max_duration_.md b/.changeset/fix_hosts_query_properly_take_into_account_the_field_usability_low_max_duration_.md new file mode 100644 index 000000000..b88db43d7 --- /dev/null +++ b/.changeset/fix_hosts_query_properly_take_into_account_the_field_usability_low_max_duration_.md @@ -0,0 +1,5 @@ +--- +default: major +--- + +# Fix hosts query, properly take into account the field 'usability_low_max_duration' diff --git a/stores/hostdb_test.go b/stores/hostdb_test.go index b31f1d40f..95914d7ec 100644 --- a/stores/hostdb_test.go +++ b/stores/hostdb_test.go @@ -320,40 +320,54 @@ func TestHosts(t *testing.T) { t.Fatal("unexpected", his[1].Checks) } - // assert usability filter is taken into account - h2c.UsabilityBreakdown.RedundantIP = true - err = ss.UpdateHostCheck(context.Background(), hk2, h2c) - if err != nil { - t.Fatal(err) - } - his, err = ss.Hosts(context.Background(), api.HostOptions{ + // use reflection to check whether usability is correctly taken into account + // for every field of the usability breakdown + opts := api.HostOptions{ FilterMode: api.HostFilterModeAll, - UsabilityMode: api.UsabilityFilterModeUsable, AddressContains: "", KeyIn: nil, Offset: 0, Limit: -1, - }) - if err != nil { - t.Fatal(err) - } else if len(his) != 1 { - t.Fatal("unexpected", len(his)) } - his, err = ss.Hosts(context.Background(), api.HostOptions{ - FilterMode: api.HostFilterModeAll, - UsabilityMode: api.UsabilityFilterModeUnusable, - AddressContains: "", - KeyIn: nil, - Offset: 0, - Limit: -1, - }) - if err != nil { - t.Fatal(err) - } else if len(his) != 1 { - t.Fatal("unexpected", len(his)) - } else if his[0].PublicKey != hk2 { - t.Fatal("unexpected") + assertHostUsability := func() error { + t.Helper() + + opts.UsabilityMode = api.UsabilityFilterModeUsable + if his, err := ss.Hosts(ctx, opts); err != nil { + return err + } else if len(his) != 1 { + return fmt.Errorf("expected one usable host, but got %d", len(his)) + } else if his[0].PublicKey != hk1 { + return fmt.Errorf("unexpected host is usable, hk %v", his[0].PublicKey) + } + + opts.UsabilityMode = api.UsabilityFilterModeUnusable + if his, err := ss.Hosts(context.Background(), opts); err != nil { + return err + } else if len(his) != 1 { + return fmt.Errorf("expected one unusable host, but got %d", len(his)) + } else if his[0].PublicKey != hk2 { + return fmt.Errorf("unexpected host is unusable, hk %v", his[0].PublicKey) + } + + return nil + } + + v := reflect.ValueOf(&h2c.UsabilityBreakdown).Elem() + for i := 0; i < v.NumField(); i++ { + field := v.Field(i) + if !field.CanSet() || field.Kind() != reflect.Bool { + continue + } + + field.SetBool(true) + if err := ss.UpdateHostCheck(ctx, hk2, h2c); err != nil { + t.Fatalf("failed to update host check after setting %s: %v", v.Type().Field(i).Name, err) + } else if err := assertHostUsability(); err != nil { + t.Fatalf("usability filter is not taken into account after setting %s: %v", v.Type().Field(i).Name, err) + } + field.SetBool(false) } // assert cascade delete on host diff --git a/stores/sql/main.go b/stores/sql/main.go index c8f2f1d8c..5e624c25c 100644 --- a/stores/sql/main.go +++ b/stores/sql/main.go @@ -755,9 +755,9 @@ func Hosts(ctx context.Context, tx sql.Tx, opts api.HostOptions) ([]api.Host, er if opts.UsabilityMode != api.UsabilityFilterModeAll { switch opts.UsabilityMode { case api.UsabilityFilterModeUsable: - whereExprs = append(whereExprs, "EXISTS (SELECT 1 FROM hosts h2 INNER JOIN host_checks hc ON hc.db_host_id = h2.id AND h2.id = h.id WHERE (hc.usability_blocked = 0 AND hc.usability_offline = 0 AND hc.usability_low_score = 0 AND hc.usability_redundant_ip = 0 AND hc.usability_gouging = 0 AND hc.usability_not_accepting_contracts = 0 AND hc.usability_not_announced = 0 AND hc.usability_not_completing_scan = 0))") + whereExprs = append(whereExprs, "EXISTS (SELECT 1 FROM hosts h2 INNER JOIN host_checks hc ON hc.db_host_id = h2.id AND h2.id = h.id WHERE (hc.usability_blocked = 0 AND hc.usability_offline = 0 AND hc.usability_low_score = 0 AND hc.usability_redundant_ip = 0 AND hc.usability_gouging = 0 AND hc.usability_low_max_duration = 0 AND hc.usability_not_accepting_contracts = 0 AND hc.usability_not_announced = 0 AND hc.usability_not_completing_scan = 0))") case api.UsabilityFilterModeUnusable: - whereExprs = append(whereExprs, "EXISTS (SELECT 1 FROM hosts h2 INNER JOIN host_checks hc ON hc.db_host_id = h2.id AND h2.id = h.id WHERE (hc.usability_blocked = 1 OR hc.usability_offline = 1 OR hc.usability_low_score = 1 OR hc.usability_redundant_ip = 1 OR hc.usability_gouging = 1 OR hc.usability_not_accepting_contracts = 1 OR hc.usability_not_announced = 1 OR hc.usability_not_completing_scan = 1))") + whereExprs = append(whereExprs, "EXISTS (SELECT 1 FROM hosts h2 INNER JOIN host_checks hc ON hc.db_host_id = h2.id AND h2.id = h.id WHERE (hc.usability_blocked = 1 OR hc.usability_offline = 1 OR hc.usability_low_score = 1 OR hc.usability_redundant_ip = 1 OR hc.usability_gouging = 1 OR hc.usability_low_max_duration = 1 OR hc.usability_not_accepting_contracts = 1 OR hc.usability_not_announced = 1 OR hc.usability_not_completing_scan = 1))") } }