Skip to content

Commit 4a1df71

Browse files
committed
fix(vibes): reject typed-nil capability implementations
1 parent 7441076 commit 4a1df71

7 files changed

Lines changed: 36 additions & 3 deletions

vibes/capability_common.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package vibes
22

33
import (
44
"fmt"
5+
"reflect"
56
"strings"
67
)
78

@@ -33,3 +34,16 @@ func validateCapabilityKwargsDataOnly(method string, kwargs map[string]Value) er
3334
}
3435
return nil
3536
}
37+
38+
func isNilCapabilityImplementation(impl any) bool {
39+
if impl == nil {
40+
return true
41+
}
42+
val := reflect.ValueOf(impl)
43+
switch val.Kind() {
44+
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice:
45+
return val.IsNil()
46+
default:
47+
return false
48+
}
49+
}

vibes/capability_db.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ func NewDBCapability(name string, db Database) (CapabilityAdapter, error) {
5353
if name == "" {
5454
return nil, fmt.Errorf("vibes: database capability name must be non-empty")
5555
}
56-
if db == nil {
56+
if isNilCapabilityImplementation(db) {
5757
return nil, fmt.Errorf("vibes: database capability requires a non-nil implementation")
5858
}
5959
return &dbCapability{name: name, db: db}, nil

vibes/capability_db_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,4 +280,9 @@ func TestNewDBCapabilityRejectsInvalidArguments(t *testing.T) {
280280
if _, err := NewDBCapability("db", db); err == nil || !strings.Contains(err.Error(), "requires a non-nil implementation") {
281281
t.Fatalf("expected nil db error, got %v", err)
282282
}
283+
284+
var typedNil *dbCapabilityStub
285+
if _, err := NewDBCapability("db", typedNil); err == nil || !strings.Contains(err.Error(), "requires a non-nil implementation") {
286+
t.Fatalf("expected typed nil db error, got %v", err)
287+
}
283288
}

vibes/capability_events.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ func NewEventsCapability(name string, publisher EventPublisher) (CapabilityAdapt
2222
if name == "" {
2323
return nil, fmt.Errorf("vibes: events capability name must be non-empty")
2424
}
25-
if publisher == nil {
25+
if isNilCapabilityImplementation(publisher) {
2626
return nil, fmt.Errorf("vibes: events capability requires a non-nil implementation")
2727
}
2828
return &eventsCapability{name: name, publisher: publisher}, nil

vibes/capability_events_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,4 +123,9 @@ func TestNewEventsCapabilityRejectsInvalidArguments(t *testing.T) {
123123
if _, err := NewEventsCapability("events", publisher); err == nil || !strings.Contains(err.Error(), "requires a non-nil implementation") {
124124
t.Fatalf("expected nil publisher error, got %v", err)
125125
}
126+
127+
var typedNil *eventsCapabilityStub
128+
if _, err := NewEventsCapability("events", typedNil); err == nil || !strings.Contains(err.Error(), "requires a non-nil implementation") {
129+
t.Fatalf("expected typed nil publisher error, got %v", err)
130+
}
126131
}

vibes/capability_jobqueue.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func NewJobQueueCapability(name string, queue JobQueue) (CapabilityAdapter, erro
4343
if name == "" {
4444
return nil, fmt.Errorf("vibes: job queue capability name must be non-empty")
4545
}
46-
if queue == nil {
46+
if isNilCapabilityImplementation(queue) {
4747
return nil, fmt.Errorf("vibes: job queue capability requires a non-nil implementation")
4848
}
4949

vibes/capability_jobqueue_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,4 +328,13 @@ func TestNewJobQueueCapabilityRejectsNilQueue(t *testing.T) {
328328
if got := err.Error(); !strings.Contains(got, "requires a non-nil implementation") {
329329
t.Fatalf("unexpected error: %s", got)
330330
}
331+
332+
var typedNil *jobQueueStub
333+
_, err = NewJobQueueCapability("jobs", typedNil)
334+
if err == nil {
335+
t.Fatalf("expected typed nil queue to fail")
336+
}
337+
if got := err.Error(); !strings.Contains(got, "requires a non-nil implementation") {
338+
t.Fatalf("unexpected typed nil error: %s", got)
339+
}
331340
}

0 commit comments

Comments
 (0)