Skip to content

Commit f11d61d

Browse files
committed
handler_blueprints_test: implement tests for validateBlueprintRequest
1 parent fca0554 commit f11d61d

File tree

1 file changed

+206
-1
lines changed

1 file changed

+206
-1
lines changed

internal/v1/handler_blueprints_test.go

Lines changed: 206 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ func TestHandlers_CreateBlueprint(t *testing.T) {
5757
if runtime.GOOS == "darwin" {
5858
t.Skip("crypt() not supported on darwin")
5959
}
60-
6160
var jsonResp v1.HTTPErrorList
6261
ctx := context.Background()
6362
dbase, srvURL, shutdownFn := makeTestServer(t, nil)
@@ -1877,3 +1876,209 @@ func TestBlueprintUpdateAddsSnapshot(t *testing.T) {
18771876

18781877
require.NoError(t, srv.DB.DeleteBlueprint(context.Background(), created.Id, "000000"))
18791878
}
1879+
1880+
func TestHandlers_BlueprintCustomizationValidation(t *testing.T) {
1881+
if runtime.GOOS == "darwin" {
1882+
t.Skip("crypt() not supported on darwin")
1883+
}
1884+
1885+
_, srvURL, shutdownFn := makeTestServer(t, nil)
1886+
defer shutdownFn(t)
1887+
1888+
// Test cases structure
1889+
testCases := []struct {
1890+
name string
1891+
customizations map[string]interface{}
1892+
expectError bool
1893+
errorTitle string
1894+
}{
1895+
// Files - Positive cases
1896+
{
1897+
name: "valid file customization - simple",
1898+
customizations: map[string]interface{}{
1899+
"files": []map[string]interface{}{
1900+
{"path": "/etc/myconfig.conf", "data": "config content"},
1901+
},
1902+
},
1903+
expectError: false,
1904+
},
1905+
{
1906+
name: "valid file customization - with mode and user",
1907+
customizations: map[string]interface{}{
1908+
"files": []map[string]interface{}{
1909+
{
1910+
"path": "/opt/app/config.json",
1911+
"data": `{"key": "value"}`,
1912+
"mode": "644",
1913+
"user": "root",
1914+
"group": "root",
1915+
},
1916+
},
1917+
},
1918+
expectError: false,
1919+
},
1920+
// Files - Negative cases
1921+
{
1922+
name: "invalid file path - relative",
1923+
customizations: map[string]interface{}{
1924+
"files": []map[string]interface{}{
1925+
{"path": "config.txt", "data": "content"},
1926+
},
1927+
},
1928+
expectError: true,
1929+
errorTitle: "Invalid file customization",
1930+
},
1931+
{
1932+
name: "invalid file path - trailing slash",
1933+
customizations: map[string]interface{}{
1934+
"files": []map[string]interface{}{
1935+
{"path": "/etc/config/", "data": "content"},
1936+
},
1937+
},
1938+
expectError: true,
1939+
errorTitle: "Invalid file customization",
1940+
},
1941+
// Directories - Positive cases
1942+
{
1943+
name: "valid directory customization - simple",
1944+
customizations: map[string]interface{}{
1945+
"directories": []map[string]interface{}{
1946+
{"path": "/opt/myapp"},
1947+
},
1948+
},
1949+
expectError: false,
1950+
},
1951+
{
1952+
name: "valid directory customization - with mode and ensure_parents",
1953+
customizations: map[string]interface{}{
1954+
"directories": []map[string]interface{}{
1955+
{
1956+
"path": "/var/lib/myservice",
1957+
"mode": "755",
1958+
"user": "service",
1959+
"group": "service",
1960+
"ensure_parents": true,
1961+
},
1962+
},
1963+
},
1964+
expectError: false,
1965+
},
1966+
// Directories - Negative cases
1967+
{
1968+
name: "invalid directory path - relative",
1969+
customizations: map[string]interface{}{
1970+
"directories": []map[string]interface{}{
1971+
{"path": "mydir"},
1972+
},
1973+
},
1974+
expectError: true,
1975+
errorTitle: "Invalid directory customization",
1976+
},
1977+
{
1978+
name: "invalid directory path - trailing slash",
1979+
customizations: map[string]interface{}{
1980+
"directories": []map[string]interface{}{
1981+
{"path": "/opt/myapp/"},
1982+
},
1983+
},
1984+
expectError: true,
1985+
errorTitle: "Invalid directory customization",
1986+
},
1987+
// Filesystem - Positive cases
1988+
{
1989+
name: "valid filesystem customization - root",
1990+
customizations: map[string]interface{}{
1991+
"filesystem": []map[string]interface{}{
1992+
{"mountpoint": "/", "min_size": 10737418240}, // 10GB
1993+
},
1994+
},
1995+
expectError: false,
1996+
},
1997+
{
1998+
name: "valid filesystem customization - var with size",
1999+
customizations: map[string]interface{}{
2000+
"filesystem": []map[string]interface{}{
2001+
{"mountpoint": "/var", "min_size": 5368709120}, // 5GB
2002+
},
2003+
},
2004+
expectError: false,
2005+
},
2006+
// Filesystem - Negative cases
2007+
{
2008+
name: "invalid filesystem mountpoint - relative",
2009+
customizations: map[string]interface{}{
2010+
"filesystem": []map[string]interface{}{
2011+
{"mountpoint": "var", "min_size": 1073741824},
2012+
},
2013+
},
2014+
expectError: true,
2015+
errorTitle: "Invalid filesystem customization",
2016+
},
2017+
{
2018+
name: "invalid filesystem mountpoint - non-canonical",
2019+
customizations: map[string]interface{}{
2020+
"filesystem": []map[string]interface{}{
2021+
{"mountpoint": "/var/../tmp", "min_size": 1073741824},
2022+
},
2023+
},
2024+
expectError: true,
2025+
errorTitle: "Invalid filesystem customization",
2026+
},
2027+
{
2028+
name: "invalid filesystem min_size - too small",
2029+
customizations: map[string]interface{}{
2030+
"filesystem": []map[string]interface{}{
2031+
{"mountpoint": "/var", "min_size": 512}, // 512 bytes, less than 1MB
2032+
},
2033+
},
2034+
expectError: true,
2035+
errorTitle: "Invalid filesystem customization",
2036+
},
2037+
}
2038+
2039+
// Run test cases
2040+
for _, tc := range testCases {
2041+
t.Run(tc.name, func(t *testing.T) {
2042+
// Create blueprint request with test customizations
2043+
body := map[string]interface{}{
2044+
"name": fmt.Sprintf("test-blueprint-%d", time.Now().UnixNano()),
2045+
"description": "Test blueprint for validation",
2046+
"customizations": tc.customizations,
2047+
"distribution": "centos-9",
2048+
"image_requests": []map[string]interface{}{
2049+
{
2050+
"architecture": "x86_64",
2051+
"image_type": "aws",
2052+
"upload_request": map[string]interface{}{
2053+
"type": "aws",
2054+
"options": map[string]interface{}{
2055+
"share_with_accounts": []string{"test-account"},
2056+
},
2057+
},
2058+
},
2059+
},
2060+
}
2061+
2062+
statusCode, resp := tutils.PostResponseBody(t, srvURL+"/api/image-builder/v1/blueprints", body)
2063+
2064+
if tc.expectError {
2065+
// Should return validation error
2066+
require.Equal(t, http.StatusUnprocessableEntity, statusCode)
2067+
2068+
var jsonResp v1.HTTPErrorList
2069+
err := json.Unmarshal([]byte(resp), &jsonResp)
2070+
require.NoError(t, err)
2071+
require.NotEmpty(t, jsonResp.Errors)
2072+
require.Equal(t, tc.errorTitle, jsonResp.Errors[0].Title)
2073+
} else {
2074+
// Should succeed
2075+
require.Equal(t, http.StatusCreated, statusCode)
2076+
2077+
var result v1.CreateBlueprintResponse
2078+
err := json.Unmarshal([]byte(resp), &result)
2079+
require.NoError(t, err)
2080+
require.NotEmpty(t, result.Id)
2081+
}
2082+
})
2083+
}
2084+
}

0 commit comments

Comments
 (0)