Skip to content

Commit ab0315b

Browse files
committed
test(envd): add tests for output truncation and orphan grandchild
Add TestStart_OrphanGrandchildDoesNotHangStream: verifies that killing a process whose grandchild holds stdout open delivers the EndEvent within the stream timeout instead of hanging.
1 parent 4e24efa commit ab0315b

1 file changed

Lines changed: 49 additions & 0 deletions

File tree

packages/envd/internal/services/process/start_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,55 @@ func TestStart_FastCommandOutputNotTruncated(t *testing.T) {
212212
}
213213
}
214214

215+
// TestStart_OrphanGrandchildDoesNotHangStream verifies that killing a
216+
// process whose child still holds stdout open does not hang the stream.
217+
// The stream must deliver the EndEvent within a reasonable time.
218+
func TestStart_OrphanGrandchildDoesNotHangStream(t *testing.T) {
219+
t.Parallel()
220+
221+
client, cleanup := newTestService(t)
222+
defer cleanup()
223+
224+
ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
225+
defer cancel()
226+
227+
// bash spawns a background child that inherits stdout and keeps
228+
// the pipe open after the parent is killed.
229+
stream, err := client.Start(ctx, connect.NewRequest(&rpc.StartRequest{
230+
Process: &rpc.ProcessConfig{
231+
Cmd: "bash",
232+
Args: []string{"-c", "sleep 300 & wait"},
233+
},
234+
}))
235+
require.NoError(t, err)
236+
237+
// Wait for start event to get PID.
238+
require.True(t, stream.Receive(), "expected start event")
239+
startEvt := stream.Msg().GetEvent().GetStart()
240+
require.NotNil(t, startEvt)
241+
pid := startEvt.GetPid()
242+
243+
// Kill the process via SendSignal (same as TestCommandKillNextApp).
244+
_, err = client.SendSignal(ctx, connect.NewRequest(&rpc.SendSignalRequest{
245+
Signal: rpc.Signal_SIGNAL_SIGKILL,
246+
Process: &rpc.ProcessSelector{
247+
Selector: &rpc.ProcessSelector_Pid{Pid: pid},
248+
},
249+
}))
250+
require.NoError(t, err)
251+
252+
var gotEnd bool
253+
for stream.Receive() {
254+
if stream.Msg().GetEvent().GetEnd() != nil {
255+
gotEnd = true
256+
}
257+
}
258+
require.NoError(t, stream.Err())
259+
_ = stream.Close()
260+
261+
assert.True(t, gotEnd, "stream should deliver EndEvent even with orphan grandchild")
262+
}
263+
215264
// processAlive checks whether a process with the given PID exists.
216265
func processAlive(pid int) bool {
217266
// /proc/<pid>/stat exists iff the process is alive (Linux-specific).

0 commit comments

Comments
 (0)