Browse code

integration-cli: move some tests to integration

Migrates:

- TestAPIErrorJSON
- TestContainerAPIInvalidPortSyntax
- TestContainerAPIRestartPolicyInvalidPolicyName
- TestContainerAPIRestartPolicyRetryMismatch
- TestContainerAPIRestartPolicyNegativeRetryCount
- TestContainerAPIRestartPolicyDefaultRetryCount
- TestCreateWithTooLowMemoryLimit

Co-authored-by: Sameer Gupta <sameergupta4873@gmail.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>

Sebastiaan van Stijn authored on 2025/10/21 01:17:32
Showing 3 changed files
... ...
@@ -644,105 +644,6 @@ func (s *DockerAPISuite) TestContainerAPIVerifyHeader(c *testing.T) {
644 644
 	_ = body.Close()
645 645
 }
646 646
 
647
-// Issue 14230. daemon should return 500 for invalid port syntax
648
-func (s *DockerAPISuite) TestContainerAPIInvalidPortSyntax(c *testing.T) {
649
-	config := `{
650
-				  "Image": "busybox",
651
-				  "HostConfig": {
652
-					"NetworkMode": "default",
653
-					"PortBindings": {
654
-					  "19039;1230": [
655
-						{}
656
-					  ]
657
-					}
658
-				  }
659
-				}`
660
-
661
-	res, body, err := request.Post(testutil.GetContext(c), "/containers/create", request.RawString(config), request.JSON)
662
-	assert.NilError(c, err)
663
-	assert.Equal(c, res.StatusCode, http.StatusBadRequest)
664
-
665
-	b, err := request.ReadBody(body)
666
-	assert.NilError(c, err)
667
-	assert.Assert(c, is.Contains(string(b[:]), "invalid port"))
668
-}
669
-
670
-func (s *DockerAPISuite) TestContainerAPIRestartPolicyInvalidPolicyName(c *testing.T) {
671
-	config := `{
672
-		"Image": "busybox",
673
-		"HostConfig": {
674
-			"RestartPolicy": {
675
-				"Name": "something",
676
-				"MaximumRetryCount": 0
677
-			}
678
-		}
679
-	}`
680
-
681
-	res, body, err := request.Post(testutil.GetContext(c), "/containers/create", request.RawString(config), request.JSON)
682
-	assert.NilError(c, err)
683
-	assert.Equal(c, res.StatusCode, http.StatusBadRequest)
684
-
685
-	b, err := request.ReadBody(body)
686
-	assert.NilError(c, err)
687
-	assert.Assert(c, is.Contains(string(b[:]), "invalid restart policy"))
688
-}
689
-
690
-func (s *DockerAPISuite) TestContainerAPIRestartPolicyRetryMismatch(c *testing.T) {
691
-	config := `{
692
-		"Image": "busybox",
693
-		"HostConfig": {
694
-			"RestartPolicy": {
695
-				"Name": "always",
696
-				"MaximumRetryCount": 2
697
-			}
698
-		}
699
-	}`
700
-
701
-	res, body, err := request.Post(testutil.GetContext(c), "/containers/create", request.RawString(config), request.JSON)
702
-	assert.NilError(c, err)
703
-	assert.Equal(c, res.StatusCode, http.StatusBadRequest)
704
-
705
-	b, err := request.ReadBody(body)
706
-	assert.NilError(c, err)
707
-	assert.Assert(c, is.Contains(string(b[:]), "invalid restart policy: maximum retry count can only be used with 'on-failure'"))
708
-}
709
-
710
-func (s *DockerAPISuite) TestContainerAPIRestartPolicyNegativeRetryCount(c *testing.T) {
711
-	config := `{
712
-		"Image": "busybox",
713
-		"HostConfig": {
714
-			"RestartPolicy": {
715
-				"Name": "on-failure",
716
-				"MaximumRetryCount": -2
717
-			}
718
-		}
719
-	}`
720
-
721
-	res, body, err := request.Post(testutil.GetContext(c), "/containers/create", request.RawString(config), request.JSON)
722
-	assert.NilError(c, err)
723
-	assert.Equal(c, res.StatusCode, http.StatusBadRequest)
724
-
725
-	b, err := request.ReadBody(body)
726
-	assert.NilError(c, err)
727
-	assert.Assert(c, is.Contains(string(b[:]), "maximum retry count cannot be negative"))
728
-}
729
-
730
-func (s *DockerAPISuite) TestContainerAPIRestartPolicyDefaultRetryCount(c *testing.T) {
731
-	config := `{
732
-		"Image": "busybox",
733
-		"HostConfig": {
734
-			"RestartPolicy": {
735
-				"Name": "on-failure",
736
-				"MaximumRetryCount": 0
737
-			}
738
-		}
739
-	}`
740
-
741
-	res, _, err := request.Post(testutil.GetContext(c), "/containers/create", request.RawString(config), request.JSON)
742
-	assert.NilError(c, err)
743
-	assert.Equal(c, res.StatusCode, http.StatusCreated)
744
-}
745
-
746 647
 // Issue 7941 - test to make sure a "null" in JSON is just ignored.
747 648
 // W/o this fix a null in JSON would be parsed into a string var as "null"
748 649
 func (s *DockerAPISuite) TestContainerAPIPostCreateNull(c *testing.T) {
... ...
@@ -789,26 +690,6 @@ func (s *DockerAPISuite) TestContainerAPIPostCreateNull(c *testing.T) {
789 789
 	assert.Equal(c, outMemorySwap, "0")
790 790
 }
791 791
 
792
-func (s *DockerAPISuite) TestCreateWithTooLowMemoryLimit(c *testing.T) {
793
-	// TODO Windows: Port once memory is supported
794
-	testRequires(c, DaemonIsLinux)
795
-	config := `{
796
-		"Image":     "busybox",
797
-		"HostConfig": {
798
-			"CpuShares": 100,
799
-			"Memory":    524287
800
-		}
801
-	}`
802
-
803
-	res, body, err := request.Post(testutil.GetContext(c), "/containers/create", request.RawString(config), request.JSON)
804
-	assert.NilError(c, err)
805
-	b, err2 := request.ReadBody(body)
806
-	assert.NilError(c, err2)
807
-
808
-	assert.Equal(c, res.StatusCode, http.StatusBadRequest)
809
-	assert.Assert(c, is.Contains(string(b), "Minimum memory limit allowed is 6MB"))
810
-}
811
-
812 792
 func (s *DockerAPISuite) TestContainerAPIKill(c *testing.T) {
813 793
 	const name = "test-api-kill"
814 794
 	runSleepingContainer(c, "-i", "--name", name)
... ...
@@ -8,7 +8,6 @@ import (
8 8
 	"github.com/moby/moby/v2/internal/testutil"
9 9
 	"github.com/moby/moby/v2/internal/testutil/request"
10 10
 	"gotest.tools/v3/assert"
11
-	is "gotest.tools/v3/assert/cmp"
12 11
 )
13 12
 
14 13
 type DockerAPISuite struct {
... ...
@@ -40,13 +39,3 @@ func (s *DockerAPISuite) TestAPIGetEnabledCORS(c *testing.T) {
40 40
 	// assert.Equal(c, res.Header.Get("Access-Control-Allow-Origin"), "*")
41 41
 	// assert.Equal(c, res.Header.Get("Access-Control-Allow-Headers"), "Origin, X-Requested-With, Content-Type, Accept, X-Registry-Auth")
42 42
 }
43
-
44
-func (s *DockerAPISuite) TestAPIErrorJSON(c *testing.T) {
45
-	httpResp, body, err := request.Post(testutil.GetContext(c), "/containers/create", request.JSONBody(struct{}{}))
46
-	assert.NilError(c, err)
47
-	assert.Equal(c, httpResp.StatusCode, http.StatusBadRequest)
48
-	assert.Assert(c, is.Contains(httpResp.Header.Get("Content-Type"), "application/json"))
49
-	b, err := request.ReadBody(body)
50
-	assert.NilError(c, err)
51
-	assert.Check(c, is.Contains(getErrorMessage(c, b), "config cannot be empty"))
52
-}
... ...
@@ -4,6 +4,7 @@ import (
4 4
 	"bufio"
5 5
 	"context"
6 6
 	"fmt"
7
+	"net/http"
7 8
 	"strconv"
8 9
 	"strings"
9 10
 	"testing"
... ...
@@ -11,6 +12,7 @@ import (
11 11
 
12 12
 	containerd "github.com/containerd/containerd/v2/client"
13 13
 	cerrdefs "github.com/containerd/errdefs"
14
+	"github.com/moby/moby/api/types/common"
14 15
 	"github.com/moby/moby/api/types/container"
15 16
 	"github.com/moby/moby/api/types/network"
16 17
 	"github.com/moby/moby/api/types/versions"
... ...
@@ -20,6 +22,7 @@ import (
20 20
 	testContainer "github.com/moby/moby/v2/integration/internal/container"
21 21
 	net "github.com/moby/moby/v2/integration/internal/network"
22 22
 	"github.com/moby/moby/v2/internal/testutil"
23
+	"github.com/moby/moby/v2/internal/testutil/request"
23 24
 	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
24 25
 	"gotest.tools/v3/assert"
25 26
 	is "gotest.tools/v3/assert/cmp"
... ...
@@ -658,6 +661,79 @@ func TestCreateInvalidHostConfig(t *testing.T) {
658 658
 	}
659 659
 }
660 660
 
661
+func TestCreateValidation(t *testing.T) {
662
+	tests := []struct {
663
+		name      string
664
+		body      string
665
+		skipOn    string
666
+		expStatus int
667
+		expError  string
668
+	}{
669
+		{
670
+			name:      "empty body",
671
+			body:      ``,
672
+			expStatus: http.StatusBadRequest,
673
+			expError:  `invalid JSON: EOF`, // TODO(thaJeztah): this could use a nicer error message.
674
+		},
675
+		{
676
+			name:      "empty config",
677
+			body:      `{}`,
678
+			expStatus: http.StatusBadRequest,
679
+			expError:  `config cannot be empty in order to create a container`,
680
+		},
681
+		{
682
+			name:      "invalid port syntax", // issue https://github.com/moby/moby/issues/14230 for invalid port syntax
683
+			body:      `{"Image": "busybox", "HostConfig": {"NetworkMode": "default", "PortBindings": {"19039;1230": [{}]}}}`,
684
+			expStatus: http.StatusBadRequest,
685
+			expError:  `invalid JSON: invalid port '19039;1230': invalid syntax`,
686
+		},
687
+		{
688
+			name:      "invalid memory-limit: value too low",
689
+			body:      `{"Image": "busybox", "HostConfig": {"CpuShares": 100, "Memory": 524287}}`,
690
+			skipOn:    "windows", // TODO Windows: Port once memory is supported
691
+			expStatus: http.StatusBadRequest,
692
+			expError:  `Minimum memory limit allowed is 6MB`,
693
+		},
694
+		{
695
+			name:      "invalid restart policy name",
696
+			body:      `{"Image": "busybox", "HostConfig": {"RestartPolicy": {"Name": "something", "MaximumRetryCount": 0}}}`,
697
+			expStatus: http.StatusBadRequest,
698
+			expError:  `invalid restart policy: unknown policy 'something'`,
699
+		},
700
+		{
701
+			name:      "invalid restart policy: retry not allowed",
702
+			body:      `{"Image": "busybox", "HostConfig": {"RestartPolicy": {"Name": "always", "MaximumRetryCount": 2}}}`,
703
+			expStatus: http.StatusBadRequest,
704
+			expError:  `invalid restart policy: maximum retry count can only be used with 'on-failure'`,
705
+		},
706
+		{
707
+			name:      "invalid restart policy: retry negative",
708
+			body:      `{"Image": "busybox", "HostConfig": {"RestartPolicy": {"Name": "on-failure", "MaximumRetryCount": -2}}}`,
709
+			expStatus: http.StatusBadRequest,
710
+			expError:  `invalid restart policy: maximum retry count cannot be negative`,
711
+		},
712
+		{
713
+			name:      "restart policy: default retry count",
714
+			body:      `{"Image": "busybox", "HostConfig": {"RestartPolicy": {"Name": "on-failure", "MaximumRetryCount": 0}}}`,
715
+			expStatus: http.StatusCreated,
716
+		},
717
+	}
718
+	for _, tc := range tests {
719
+		t.Run(tc.name, func(t *testing.T) {
720
+			skip.If(t, testEnv.DaemonInfo.OSType == tc.skipOn)
721
+			res, _, err := request.Post(testutil.GetContext(t), "/containers/create", request.RawString(tc.body), request.JSON)
722
+			assert.NilError(t, err)
723
+			assert.Equal(t, res.StatusCode, tc.expStatus)
724
+
725
+			if tc.expError != "" {
726
+				var respErr common.ErrorResponse
727
+				assert.NilError(t, request.ReadJSONResponse(res, &respErr))
728
+				assert.ErrorContains(t, respErr, tc.expError)
729
+			}
730
+		})
731
+	}
732
+}
733
+
661 734
 func TestCreateWithMultipleEndpointSettings(t *testing.T) {
662 735
 	ctx := setupTest(t)
663 736