Tests
This commit is contained in:
		
							parent
							
								
									0080ea5a20
								
							
						
					
					
						commit
						a160da3ad9
					
				
					 4 changed files with 100 additions and 83 deletions
				
			
		|  | @ -47,7 +47,7 @@ var cmdPublish = &cli.Command{ | |||
| 	Aliases: []string{"pub", "send", "trigger"}, | ||||
| 	Usage:   "Send message via a ntfy server", | ||||
| 	UsageText: `ntfy publish [OPTIONS..] TOPIC [MESSAGE...] | ||||
| ntfy publish [OPTIONS..] --wait-cmd -P COMMAND... | ||||
| ntfy publish [OPTIONS..] --wait-cmd COMMAND... | ||||
| NTFY_TOPIC=.. ntfy publish [OPTIONS..] -P [MESSAGE...]`, | ||||
| 	Action:   execPublish, | ||||
| 	Category: categoryClient, | ||||
|  | @ -156,18 +156,18 @@ func execPublish(c *cli.Context) error { | |||
| 		options = append(options, client.WithBasicAuth(user, pass)) | ||||
| 	} | ||||
| 	if pid > 0 { | ||||
| 		if err := waitForProcess(pid); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if message == "" { | ||||
| 			message = fmt.Sprintf("process with PID %d exited", pid) | ||||
| 		} | ||||
| 	} else if len(command) > 0 { | ||||
| 		cmdResultMessage, err := runAndWaitForCommand(command) | ||||
| 		newMessage, err := waitForProcess(pid) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} else if message == "" { | ||||
| 			message = cmdResultMessage | ||||
| 			message = newMessage | ||||
| 		} | ||||
| 	} else if len(command) > 0 { | ||||
| 		newMessage, err := runAndWaitForCommand(command) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} else if message == "" { | ||||
| 			message = newMessage | ||||
| 		} | ||||
| 	} | ||||
| 	var body io.Reader | ||||
|  | @ -203,11 +203,14 @@ func execPublish(c *cli.Context) error { | |||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // parseTopicMessageCommand reads the topic and the remaining arguments from the context. | ||||
| 
 | ||||
| // There are a few cases to consider: | ||||
| //   ntfy publish <topic> [<message>] | ||||
| //   ntfy publish --wait-cmd <topic> <command> | ||||
| //   NTFY_TOPIC=.. ntfy publish [<message>] | ||||
| //   NTFY_TOPIC=.. ntfy publish --wait-cmd <command> | ||||
| func parseTopicMessageCommand(c *cli.Context) (topic string, message string, command []string, err error) { | ||||
| 	// 1. ntfy publish --wait-cmd <topic> <command> | ||||
| 	// 2. NTFY_TOPIC=.. ntfy publish --wait-cmd <command> | ||||
| 	// 3. ntfy publish <topic> [<message>] | ||||
| 	// 4. NTFY_TOPIC=.. ntfy publish [<message>] | ||||
| 	var args []string | ||||
| 	topic, args, err = parseTopicAndArgs(c) | ||||
| 	if err != nil { | ||||
|  | @ -251,35 +254,39 @@ func remainingArgs(c *cli.Context, fromIndex int) []string { | |||
| 	return []string{} | ||||
| } | ||||
| 
 | ||||
| func waitForProcess(pid int) error { | ||||
| func waitForProcess(pid int) (message string, err error) { | ||||
| 	if !processExists(pid) { | ||||
| 		return fmt.Errorf("process with PID %d not running", pid) | ||||
| 		return "", fmt.Errorf("process with PID %d not running", pid) | ||||
| 	} | ||||
| 	start := time.Now() | ||||
| 	log.Debug("Waiting for process with PID %d to exit", pid) | ||||
| 	for processExists(pid) { | ||||
| 		time.Sleep(500 * time.Millisecond) | ||||
| 	} | ||||
| 	log.Debug("Process with PID %d exited", pid) | ||||
| 	return nil | ||||
| 	runtime := time.Since(start).Round(time.Millisecond) | ||||
| 	log.Debug("Process with PID %d exited after %s", pid, runtime) | ||||
| 	return fmt.Sprintf("Process with PID %d exited after %s", pid, runtime), nil | ||||
| } | ||||
| 
 | ||||
| func runAndWaitForCommand(command []string) (message string, err error) { | ||||
| 	prettyCmd := util.QuoteCommand(command) | ||||
| 	log.Debug("Running command: %s", prettyCmd) | ||||
| 	start := time.Now() | ||||
| 	cmd := exec.Command(command[0], command[1:]...) | ||||
| 	if log.IsTrace() { | ||||
| 		cmd.Stdout = os.Stdout | ||||
| 		cmd.Stderr = os.Stderr | ||||
| 	} | ||||
| 	if err := cmd.Run(); err != nil { | ||||
| 	err = cmd.Run() | ||||
| 	runtime := time.Since(start).Round(time.Millisecond) | ||||
| 	if err != nil { | ||||
| 		if exitError, ok := err.(*exec.ExitError); ok { | ||||
| 			message = fmt.Sprintf("Command failed (exit code %d): %s", exitError.ExitCode(), prettyCmd) | ||||
| 		} else { | ||||
| 			message = fmt.Sprintf("Command failed: %s, error: %s", prettyCmd, err.Error()) | ||||
| 			log.Debug("Command failed after %s (exit code %d): %s", runtime, exitError.ExitCode(), prettyCmd) | ||||
| 			return fmt.Sprintf("Command failed after %s (exit code %d): %s", runtime, exitError.ExitCode(), prettyCmd), nil | ||||
| 		} | ||||
| 	} else { | ||||
| 		message = fmt.Sprintf("Command done: %s", prettyCmd) | ||||
| 		// Hard fail when command does not exist or could not be properly launched | ||||
| 		return "", fmt.Errorf("command failed: %s, error: %s", prettyCmd, err.Error()) | ||||
| 	} | ||||
| 	log.Debug(message) | ||||
| 	return message, nil | ||||
| 	log.Debug("Command succeeded after %s: %s", runtime, prettyCmd) | ||||
| 	return fmt.Sprintf("Command succeeded after %s: %s", runtime, prettyCmd), nil | ||||
| } | ||||
|  |  | |||
|  | @ -5,7 +5,11 @@ import ( | |||
| 	"github.com/stretchr/testify/require" | ||||
| 	"heckel.io/ntfy/test" | ||||
| 	"heckel.io/ntfy/util" | ||||
| 	"os" | ||||
| 	"os/exec" | ||||
| 	"strconv" | ||||
| 	"testing" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| func TestCLI_Publish_Subscribe_Poll_Real_Server(t *testing.T) { | ||||
|  | @ -70,3 +74,66 @@ func TestCLI_Publish_All_The_Things(t *testing.T) { | |||
| 	require.Equal(t, int64(0), m.Attachment.Expires) | ||||
| 	require.Equal(t, "", m.Attachment.Type) | ||||
| } | ||||
| 
 | ||||
| func TestCLI_Publish_Wait_PID_And_Cmd(t *testing.T) { | ||||
| 	s, port := test.StartServer(t) | ||||
| 	defer test.StopServer(t, s, port) | ||||
| 	topic := fmt.Sprintf("http://127.0.0.1:%d/mytopic", port) | ||||
| 
 | ||||
| 	// Test: sleep 0.5 | ||||
| 	sleep := exec.Command("sleep", "0.5") | ||||
| 	require.Nil(t, sleep.Start()) | ||||
| 	go sleep.Wait() // Must be called to release resources | ||||
| 	start := time.Now() | ||||
| 	app, _, stdout, _ := newTestApp() | ||||
| 	require.Nil(t, app.Run([]string{"ntfy", "publish", "--wait-pid", strconv.Itoa(sleep.Process.Pid), topic})) | ||||
| 	m := toMessage(t, stdout.String()) | ||||
| 	require.True(t, time.Since(start) >= 500*time.Millisecond) | ||||
| 	require.Regexp(t, `Process with PID \d+ exited after `, m.Message) | ||||
| 
 | ||||
| 	// Test: PID does not exist | ||||
| 	app, _, _, _ = newTestApp() | ||||
| 	err := app.Run([]string{"ntfy", "publish", "--wait-pid", "1234567", topic}) | ||||
| 	require.Error(t, err) | ||||
| 	require.Equal(t, "process with PID 1234567 not running", err.Error()) | ||||
| 
 | ||||
| 	// Test: Successful command (exit 0) | ||||
| 	start = time.Now() | ||||
| 	app, _, stdout, _ = newTestApp() | ||||
| 	require.Nil(t, app.Run([]string{"ntfy", "publish", "--wait-cmd", topic, "sleep", "0.5"})) | ||||
| 	m = toMessage(t, stdout.String()) | ||||
| 	require.True(t, time.Since(start) >= 500*time.Millisecond) | ||||
| 	require.Contains(t, m.Message, `Command succeeded after `) | ||||
| 	require.Contains(t, m.Message, `: sleep 0.5`) | ||||
| 
 | ||||
| 	// Test: Failing command (exit 1) | ||||
| 	app, _, stdout, _ = newTestApp() | ||||
| 	require.Nil(t, app.Run([]string{"ntfy", "publish", "--wait-cmd", topic, "/bin/false", "false doesn't care about its args"})) | ||||
| 	m = toMessage(t, stdout.String()) | ||||
| 	require.Contains(t, m.Message, `Command failed after `) | ||||
| 	require.Contains(t, m.Message, `(exit code 1): /bin/false "false doesn't care about its args"`, m.Message) | ||||
| 
 | ||||
| 	// Test: Non-existing command (hard fail!) | ||||
| 	app, _, _, _ = newTestApp() | ||||
| 	err = app.Run([]string{"ntfy", "publish", "--wait-cmd", topic, "does-not-exist-no-really", "really though"}) | ||||
| 	require.Error(t, err) | ||||
| 	require.Equal(t, `command failed: does-not-exist-no-really "really though", error: exec: "does-not-exist-no-really": executable file not found in $PATH`, err.Error()) | ||||
| 
 | ||||
| 	// Tests with NTFY_TOPIC set //// | ||||
| 	require.Nil(t, os.Setenv("NTFY_TOPIC", topic)) | ||||
| 
 | ||||
| 	// Test: Successful command with NTFY_TOPIC | ||||
| 	app, _, stdout, _ = newTestApp() | ||||
| 	require.Nil(t, app.Run([]string{"ntfy", "publish", "--env-topic", "--cmd", "echo", "hi there"})) | ||||
| 	m = toMessage(t, stdout.String()) | ||||
| 	require.Equal(t, "mytopic", m.Topic) | ||||
| 
 | ||||
| 	// Test: Successful --wait-pid with NTFY_TOPIC | ||||
| 	sleep = exec.Command("sleep", "0.2") | ||||
| 	require.Nil(t, sleep.Start()) | ||||
| 	go sleep.Wait() // Must be called to release resources | ||||
| 	app, _, stdout, _ = newTestApp() | ||||
| 	require.Nil(t, app.Run([]string{"ntfy", "publish", "--env-topic", "--wait-pid", strconv.Itoa(sleep.Process.Pid)})) | ||||
| 	m = toMessage(t, stdout.String()) | ||||
| 	require.Regexp(t, `Process with PID \d+ exited after .+ms`, m.Message) | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue