Skip to content

Commit c0cbd6d

Browse files
committed
implement DooD for startApp test first part
1 parent 62e5944 commit c0cbd6d

File tree

4 files changed

+461
-0
lines changed

4 files changed

+461
-0
lines changed

Taskfile.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ tasks:
5757
test:update:
5858
cmds:
5959
- go test --timeout 30m -v ./internal/e2e/updatetest
60+
#todo se funziona rimuovere e integrare con il comando qui sopra!
61+
test:start:
62+
cmds:
63+
- go test --timeout 10m -v ./internal/e2e/startapp
6064

6165
test:pkg:
6266
desc: Run only tests in the pkg directory

internal/e2e/startapp/helper.go

Lines changed: 356 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,356 @@
1+
package startapp
2+
3+
import (
4+
"bufio"
5+
"bytes"
6+
"context"
7+
"encoding/json"
8+
"fmt"
9+
"iter"
10+
"log"
11+
"net"
12+
"net/http"
13+
"os"
14+
"os/exec"
15+
"path/filepath"
16+
"strconv"
17+
"strings"
18+
"testing"
19+
"time"
20+
21+
"github.com/stretchr/testify/require"
22+
)
23+
24+
func fetchDebPackageLatest(t *testing.T, path, repo string) string {
25+
t.Helper()
26+
27+
repo = fmt.Sprintf("github.com/arduino/%s", repo)
28+
cmd := exec.Command(
29+
"gh", "release", "list",
30+
"--repo", repo,
31+
"--exclude-pre-releases",
32+
"--limit", "1",
33+
)
34+
35+
output, err := cmd.CombinedOutput()
36+
if err != nil {
37+
log.Fatalf("command failed: %v\nOutput: %s", err, output)
38+
}
39+
40+
fmt.Println(string(output))
41+
42+
fields := strings.Fields(string(output))
43+
if len(fields) == 0 {
44+
log.Fatal("could not parse tag from gh release list output")
45+
}
46+
tag := fields[0]
47+
48+
fmt.Println("Detected tag:", tag)
49+
cmd2 := exec.Command(
50+
"gh", "release", "download",
51+
tag,
52+
"--repo", repo,
53+
"--pattern", "*.deb",
54+
"--dir", path,
55+
)
56+
57+
out, err := cmd2.CombinedOutput()
58+
if err != nil {
59+
log.Fatalf("download failed: %v\nOutput: %s", err, out)
60+
}
61+
62+
return tag
63+
64+
}
65+
66+
func buildDebVersion(t *testing.T, storePath, tagVersion, arch string) {
67+
t.Helper()
68+
cwd, err := os.Getwd()
69+
if err != nil {
70+
panic(err)
71+
}
72+
outputDir := filepath.Join(cwd, storePath)
73+
74+
tagVersion = fmt.Sprintf("VERSION=%s", tagVersion)
75+
arch = fmt.Sprintf("ARCH=%s", arch)
76+
outputDir = fmt.Sprintf("OUTPUT=%s", outputDir)
77+
78+
cmd := exec.Command(
79+
"go", "tool", "task", "build-deb",
80+
tagVersion,
81+
arch,
82+
outputDir,
83+
)
84+
85+
if err := cmd.Run(); err != nil {
86+
log.Fatalf("failed to run build command: %v", err)
87+
}
88+
}
89+
90+
func genMajorTag(t *testing.T, tag string) string {
91+
t.Helper()
92+
93+
parts := strings.Split(tag, ".")
94+
last := parts[len(parts)-1]
95+
96+
lastNum, _ := strconv.Atoi(strings.TrimPrefix(last, "v"))
97+
lastNum++
98+
99+
parts[len(parts)-1] = strconv.Itoa(lastNum)
100+
newTag := strings.Join(parts, ".")
101+
102+
return newTag
103+
}
104+
105+
func genMinorTag(t *testing.T, tag string) string {
106+
t.Helper()
107+
108+
parts := strings.Split(tag, ".")
109+
last := parts[len(parts)-1]
110+
111+
lastNum, _ := strconv.Atoi(strings.TrimPrefix(last, "v"))
112+
if lastNum > 0 {
113+
lastNum--
114+
}
115+
116+
parts[len(parts)-1] = strconv.Itoa(lastNum)
117+
newTag := strings.Join(parts, ".")
118+
119+
if !strings.HasPrefix(newTag, "v") {
120+
newTag = "v" + newTag
121+
}
122+
return newTag
123+
}
124+
125+
func buildDockerImage(t *testing.T, dockerfile, name, arch, dockerGid string) {
126+
t.Helper()
127+
128+
archArg := fmt.Sprintf("ARCH=%s", arch)
129+
gidArg := fmt.Sprintf("DOCKER_GID=%s", dockerGid)
130+
131+
cmd := exec.Command("docker", "build",
132+
"--build-arg", archArg,
133+
"--build-arg", gidArg,
134+
"-t", name, "-f", dockerfile, ".")
135+
// Capture both stdout and stderr
136+
var out bytes.Buffer
137+
var stderr bytes.Buffer
138+
cmd.Stdout = &out
139+
cmd.Stderr = &stderr
140+
141+
err := cmd.Run()
142+
if err != nil {
143+
fmt.Printf("❌ Docker build failed: %v\n", err)
144+
fmt.Printf("---- STDERR ----\n%s\n", stderr.String())
145+
fmt.Printf("---- STDOUT ----\n%s\n", out.String())
146+
return
147+
}
148+
149+
fmt.Println("✅ Docker build succeeded!")
150+
}
151+
152+
func startDockerContainer(t *testing.T, containerName string, containerImageName string) {
153+
t.Helper()
154+
155+
cmd := exec.Command(
156+
"docker", "run", "--rm", "-d",
157+
"-p", "8800:8800",
158+
"--privileged",
159+
"--cgroupns=host",
160+
//"--network", "host",
161+
"-v", "/sys/fs/cgroup:/sys/fs/cgroup:rw",
162+
"-v", "/var/run/docker.sock:/var/run/docker.sock",
163+
"-e", "DOCKER_HOST=unix:///var/run/docker.sock",
164+
"--name", containerName,
165+
containerImageName,
166+
)
167+
168+
if err := cmd.Run(); err != nil {
169+
t.Fatalf("failed to run container: %v", err)
170+
}
171+
172+
}
173+
174+
func getAppCliVersion(t *testing.T, containerName string) string {
175+
t.Helper()
176+
177+
cmd := exec.Command(
178+
"docker", "exec",
179+
"--user", "arduino",
180+
containerName,
181+
"arduino-app-cli", "version", "--format", "json",
182+
)
183+
output, err := cmd.CombinedOutput()
184+
if err != nil {
185+
log.Fatalf("command failed: %v\nOutput: %s", err, output)
186+
}
187+
188+
var version struct {
189+
Version string `json:"version"`
190+
DaemonVersion string `json:"daemon_version"`
191+
}
192+
err = json.Unmarshal(output, &version)
193+
require.NoError(t, err)
194+
// TODO to enable after 0.6.7
195+
// require.Equal(t, version.Version, version.DaemonVersion, "client and daemon versions should match")
196+
require.NotEmpty(t, version.Version)
197+
return version.Version
198+
199+
}
200+
201+
func runSystemUpdate(t *testing.T, containerName string) {
202+
t.Helper()
203+
204+
cmd := exec.Command(
205+
"docker", "exec",
206+
"--user", "arduino",
207+
containerName,
208+
"arduino-app-cli", "system", "update", "--yes",
209+
)
210+
output, err := cmd.CombinedOutput()
211+
require.NoError(t, err, "system update failed: %s", output)
212+
t.Logf("system update output: %s", output)
213+
}
214+
215+
func stopDockerContainer(t *testing.T, containerName string) {
216+
t.Helper()
217+
218+
cleanupCmd := exec.Command("docker", "rm", "-f", containerName)
219+
220+
fmt.Println("🧹 Removing Docker container " + containerName)
221+
if err := cleanupCmd.Run(); err != nil {
222+
fmt.Printf("⚠️ Warning: could not remove container (might not exist): %v\n", err)
223+
}
224+
225+
}
226+
227+
func putUpdateRequest(t *testing.T, host string) {
228+
229+
t.Helper()
230+
231+
url := fmt.Sprintf("http://%s/v1/system/update/apply", host)
232+
233+
req, err := http.NewRequest(http.MethodPut, url, nil)
234+
if err != nil {
235+
log.Fatalf("Error creating request: %v", err)
236+
}
237+
238+
req.Header.Set("Content-Type", "application/json")
239+
240+
client := &http.Client{}
241+
resp, err := client.Do(req)
242+
if err != nil {
243+
log.Fatalf("Error sending request: %v", err)
244+
}
245+
defer resp.Body.Close()
246+
247+
require.Equal(t, 202, resp.StatusCode)
248+
249+
}
250+
251+
func NewSSEClient(ctx context.Context, method, url string) iter.Seq2[Event, error] {
252+
return func(yield func(Event, error) bool) {
253+
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
254+
if err != nil {
255+
_ = yield(Event{}, err)
256+
return
257+
}
258+
259+
resp, err := http.DefaultClient.Do(req)
260+
if err != nil {
261+
_ = yield(Event{}, err)
262+
return
263+
}
264+
defer resp.Body.Close()
265+
266+
if resp.StatusCode != 200 {
267+
_ = yield(Event{}, fmt.Errorf("got response status code %d", resp.StatusCode))
268+
return
269+
}
270+
271+
reader := bufio.NewReader(resp.Body)
272+
273+
evt := Event{}
274+
for {
275+
line, err := reader.ReadString('\n')
276+
if err != nil {
277+
_ = yield(Event{}, err)
278+
return
279+
}
280+
switch {
281+
case strings.HasPrefix(line, "data:"):
282+
evt.Data = []byte(strings.TrimSpace(strings.TrimPrefix(line, "data:")))
283+
case strings.HasPrefix(line, "event:"):
284+
evt.Event = strings.TrimSpace(strings.TrimPrefix(line, "event:"))
285+
case strings.HasPrefix(line, "id:"):
286+
evt.ID = strings.TrimSpace(strings.TrimPrefix(line, "id:"))
287+
case strings.HasPrefix(line, "\n"):
288+
if !yield(evt, nil) {
289+
return
290+
}
291+
evt = Event{}
292+
default:
293+
_ = yield(Event{}, fmt.Errorf("unknown line: '%s'", line))
294+
return
295+
}
296+
}
297+
}
298+
}
299+
300+
type Event struct {
301+
ID string
302+
Event string
303+
Data []byte // json
304+
}
305+
306+
func waitForPort(t *testing.T, host string, timeout time.Duration) { // nolint:unparam
307+
t.Helper()
308+
deadline := time.Now().Add(timeout)
309+
for time.Now().Before(deadline) {
310+
conn, err := net.DialTimeout("tcp", host, 500*time.Millisecond)
311+
if err == nil {
312+
_ = conn.Close()
313+
t.Logf("Server is up on %s", host)
314+
return
315+
}
316+
time.Sleep(200 * time.Millisecond)
317+
}
318+
t.Fatalf("Server at %s did not start within %v", host, timeout)
319+
}
320+
321+
func runAppStart(t *testing.T, containerName, appName string) {
322+
t.Helper()
323+
appCommand := fmt.Sprintf("arduino-app-cli app start %s", appName)
324+
325+
cmd := exec.Command(
326+
"docker", "exec",
327+
containerName,
328+
"su", "-", "arduino",
329+
"-c", appCommand,
330+
)
331+
332+
output, err := cmd.CombinedOutput()
333+
require.NoError(t, err, "start command filed: %s", output)
334+
t.Logf("Output comando 'start': %s", output)
335+
}
336+
337+
func checkContainerRunningOnHost(t *testing.T, appContainerName string) bool {
338+
t.Helper()
339+
cmd := exec.Command(
340+
"docker", "ps",
341+
"--filter", "name=^/"+appContainerName+"$",
342+
"--format", "{{.Names}}",
343+
)
344+
345+
var stdout bytes.Buffer
346+
cmd.Stdout = &stdout
347+
348+
err := cmd.Run()
349+
require.NoError(t, err, "fallito controllo 'docker ps' sull'host")
350+
351+
return strings.TrimSpace(stdout.String()) == appContainerName
352+
}
353+
func stopAppContainer(t *testing.T, appContainerName string) {
354+
t.Helper()
355+
stopDockerContainer(t, appContainerName)
356+
}

0 commit comments

Comments
 (0)