

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

# AWS CloudFormation Guard 규칙 테스트
<a name="testing-rules"></a>

기본 제공 단위 테스트 프레임워크를 AWS CloudFormation Guard 사용하여 Guard 규칙이 의도한 대로 작동하는지 확인할 수 있습니다. 이 섹션에서는 단위 테스트 파일을 작성하는 방법과 `test` 명령을 사용하여 규칙 파일을 테스트하는 데 사용하는 방법을 안내합니다.

단위 테스트 파일에는 `.json`, , , `.JSON`, 또는 확장명 중 하나가 있어야 합니다`.jsn``.yaml``.YAML``.yml`.

**Topics**
+ [사전 조건](#testing-rules-prerequisites)
+ [Guard 유닛 테스트 파일 개요](#testing-rules-overview)
+ [Guard 규칙 단위 테스트 파일 작성 연습](#testing-rules-example)

## 사전 조건
<a name="testing-rules-prerequisites"></a>

입력 데이터를 평가할 Guard 규칙을 작성합니다. 자세한 내용은 [Guard 규칙 작성](writing-rules.md) 단원을 참조하십시오.

## Guard 유닛 테스트 파일 개요
<a name="testing-rules-overview"></a>

가드 단위 테스트 파일은 여러 입력과 Guard 규칙 파일 내에 작성된 규칙에 대한 예상 결과를 포함하는 JSON 또는 YAML 형식의 파일입니다. 다양한 기대치를 평가하기 위한 여러 샘플이 있을 수 있습니다. 먼저 빈 입력을 테스트한 다음 다양한 규칙 및 절을 평가하기 위한 정보를 점진적으로 추가하는 것이 좋습니다.

또한 접미사 `_test.json` 또는를 사용하여 단위 테스트 파일의 이름을 지정하는 것이 좋습니다`_tests.yaml`. 예를 들어 라는 규칙 파일이 있는 경우 단위 테스트 파일 `my_rules.guard`이름을 로 지정합니다`my_rules_tests.yaml`.

### 구문
<a name="testing-rules-syntax"></a>

다음은 단위 테스트 파일의 구문을 YAML 형식으로 보여줍니다.

```
---
- name: <TEST NAME>
  input:
     <SAMPLE INPUT>
   expectations:
     rules:
       <RULE NAME>: [PASS|FAIL|SKIP]
```

### 속성
<a name="testing-rules-properties"></a>

다음은 Guard 테스트 파일의 속성입니다.

`input`  <a name="testing-rules-properties-input"></a>
규칙을 테스트할 데이터입니다. 다음 예제와 같이 첫 번째 테스트에서 빈 입력을 사용하는 것이 좋습니다.  

```
---
- name: MyTest1
  input {}
```
후속 테스트의 경우 테스트할 입력 데이터를 추가합니다.  
 *필수 항목 여부:* 예 

`expectations`  <a name="testing-rules-properties-expectations"></a>
특정 규칙이 입력 데이터에 대해 평가될 때 예상되는 결과입니다. 각 규칙에 대한 예상 결과 외에도 테스트하려는 하나 이상의 규칙을 지정합니다. 예상 결과는 다음 중 하나여야 합니다.  
+ `PASS` - 입력 데이터에 대해 실행하면 규칙이 로 평가됩니다`true`.
+ `FAIL` - 입력 데이터에 대해 실행하면 규칙이 로 평가됩니다`false`.
+ `SKIP` - 입력 데이터에 대해 실행하면 규칙이 트리거되지 않습니다.

```
expectations:
    rules:
      check_rest_api_is_private: PASS
```
 *필수 항목 여부:* 예 

## Guard 규칙 단위 테스트 파일 작성 연습
<a name="testing-rules-example"></a>

다음은 라는 규칙 파일입니다`api_gateway_private.guard`. 이 규칙의 목적은 CloudFormation 템플릿에 정의된 모든 Amazon API Gateway 리소스 유형이 프라이빗 액세스용으로만 배포되었는지 확인하는 것입니다. 또한 하나 이상의 정책 문이 Virtual Private Cloud(VPC)에서 액세스를 허용하는지 여부를 확인합니다.

```
#
# Select all AWS::ApiGateway::RestApi resources
#     present in the Resources section of the template. 
#
let api_gws = Resources.*[ Type == 'AWS::ApiGateway::RestApi']

#
# Rule intent:         
# 1) All AWS::ApiGateway::RestApi resources deployed must be private.                                            
# 2) All AWS::ApiGateway::RestApi resources deployed must have at least one AWS Identity and Access Management (IAM) policy condition key to allow access from a VPC.
#
# Expectations:        
# 1) SKIP when there are no AWS::ApiGateway::RestApi resources in the template.  
# 2) PASS when:
#     ALL AWS::ApiGateway::RestApi resources in the template have the EndpointConfiguration property set to Type: PRIVATE. 
#     ALL AWS::ApiGateway::RestApi resources in the template have one IAM condition key specified in the Policy property with aws:sourceVpc or :SourceVpc.    
# 3) FAIL otherwise.                                                                                  
#
#

rule check_rest_api_is_private when %api_gws !empty {      
    %api_gws {
        Properties.EndpointConfiguration.Types[*] == "PRIVATE"                             
    }  
}       

rule check_rest_api_has_vpc_access when check_rest_api_is_private {
    %api_gws {
        Properties {
            #
            # ALL AWS::ApiGateway::RestApi resources in the template have one IAM condition key specified in the Policy property with 
            #     aws:sourceVpc or :SourceVpc
            #           
            some Policy.Statement[*] {
                Condition.*[ keys == /aws:[sS]ource(Vpc|VPC|Vpce|VPCE)/ ] !empty
            }
        }
    }
}
```

이 연습에서는 첫 번째 규칙 의도를 테스트합니다. 배포된 모든 `AWS::ApiGateway::RestApi` 리소스는 프라이빗이어야 합니다.

1. 다음과 같은 초기 테스트`api_gateway_private_tests.yaml`가 포함된 라는 단위 테스트 파일을 생성합니다. 초기 테스트에서는 빈 입력을 추가하고 입력으로 `AWS::ApiGateway::RestApi` 리소스가 없기 때문에 규칙이 건너뛸 것으로 예상`check_rest_api_is_private`합니다.

   ```
   ---
   - name: MyTest1
     input: {}
     expectations:
       rules:
         check_rest_api_is_private: SKIP
   ```

1. `test` 명령을 사용하여 터미널에서 첫 번째 테스트를 실행합니다. `--rules-file` 파라미터에 규칙 파일을 지정합니다. `--test-data` 파라미터에 단위 테스트 파일을 지정합니다.

   ```
   cfn-guard test --rules-file api_gateway_private.guard --test-data api_gateway_private_tests.yaml
   ```

   첫 번째 테스트의 결과는 입니다`PASS`.

   ```
   Test Case #1
   Name: "MyTest1"
     PASS Rules:
       check_rest_api_is_private: Expected = SKIP, Evaluated = SKIP
   ```

1. 단위 테스트 파일에 다른 테스트를 추가합니다. 이제 빈 리소스를 포함하도록 테스트를 확장합니다. 다음은 업데이트된 `api_gateway_private_tests.yaml` 파일입니다.

   ```
   ---
   - name: MyTest1
     input: {}
     expectations:
       rules:
         check_rest_api_is_private: SKIP
   - name: MyTest2
     input:
        Resources: {}
     expectations:
       rules:
         check_rest_api_is_private: SKIP
   ```

1. 업데이트된 단위 테스트 파일로 `test`를 실행합니다.

   ```
   cfn-guard test --rules-file api_gateway_private.guard --test-data api_gateway_private_tests.yaml
   ```

   두 번째 테스트의 결과는 입니다`PASS`.

   ```
   Test Case #1
   Name: "MyTest1"
     PASS Rules:
       check_rest_api_is_private: Expected = SKIP, Evaluated = SKIP
   Test Case #2
   Name: "MyTest2"
     PASS Rules:
       check_rest_api_is_private: Expected = SKIP, Evaluated = SKIP
   ```

1. 단위 테스트 파일에 두 개의 테스트를 추가합니다. 다음을 포함하도록 테스트를 확장합니다.
   + 속성이 지정되지 않은 `AWS::ApiGateway::RestApi` 리소스입니다.
**참고**  
이는 유효한 CloudFormation 템플릿은 아니지만 잘못된 형식의 입력에 대해서도 규칙이 올바르게 작동하는지 테스트하는 것이 유용합니다.

     `EndpointConfiguration` 속성이 지정되지 않아 로 설정되지 않았으므로이 테스트가 실패할 것으로 예상됩니다`PRIVATE`.
   + `EndpointConfiguration` 속성이 로 설정된 첫 번째 의도를 충족하지`PRIVATE`만 정책 문이 정의되지 않았기 때문에 두 번째 의도를 충족하지 않는 `AWS::ApiGateway::RestApi` 리소스입니다. 이 테스트가 통과할 것으로 예상합니다.

   다음은 업데이트된 단위 테스트 파일입니다.

   ```
   ---
   - name: MyTest1
     input: {}
     expectations:
       rules:
         check_rest_api_is_private: SKIP
   - name: MyTest2
     input:
        Resources: {}
     expectations:
       rules:
         check_rest_api_is_private: SKIP
   - name: MyTest3
     input:
       Resources: 
         apiGw:
           Type: AWS::ApiGateway::RestApi
     expectations:
       rules:
         check_rest_api_is_private: FAIL
   - name: MyTest4
     input:
       Resources: 
         apiGw:
           Type: AWS::ApiGateway::RestApi
           Properties:
             EndpointConfiguration:
               Types: "PRIVATE"
     expectations:
       rules:
         check_rest_api_is_private: PASS
   ```

1. 업데이트된 단위 테스트 파일로 `test`를 실행합니다.

   ```
   cfn-guard test --rules-file api_gateway_private.guard --test-data api_gateway_private_tests.yaml \
   ```

   세 번째 결과는 이고 `FAIL`네 번째 결과는 입니다`PASS`.

   ```
   Test Case #1
   Name: "MyTest1"
     PASS Rules:
       check_rest_api_is_private: Expected = SKIP, Evaluated = SKIP
   
   Test Case #2
   Name: "MyTest2"
     PASS Rules:
       check_rest_api_is_private: Expected = SKIP, Evaluated = SKIP
   
   Test Case #3
   Name: "MyTest3"
     PASS Rules:
       check_rest_api_is_private: Expected = FAIL, Evaluated = FAIL
   
   Test Case #4
   Name: "MyTest4"
     PASS Rules:
       check_rest_api_is_private: Expected = PASS, Evaluated = PASS
   ```

1. 단위 테스트 파일에 테스트 1\$13을 주석 처리합니다. 네 번째 테스트에 대해서만 상세 컨텍스트에 액세스합니다. 다음은 업데이트된 단위 테스트 파일입니다.

   ```
   ---
   #- name: MyTest1
   #  input: {}
   #  expectations:
   #    rules:
   #      check_rest_api_is_private_and_has_access: SKIP
   #- name: MyTest2
   #  input:
   #     Resources: {}
   #  expectations:
   #    rules:
   #      check_rest_api_is_private_and_has_access: SKIP
   #- name: MyTest3
   #  input:
   #    Resources: 
   #      apiGw:
   #        Type: AWS::ApiGateway::RestApi
   #  expectations:
   #    rules:
   #      check_rest_api_is_private_and_has_access: FAIL
   - name: MyTest4
     input:
       Resources: 
         apiGw:
           Type: AWS::ApiGateway::RestApi
           Properties:
             EndpointConfiguration:
               Types: "PRIVATE"
     expectations:
       rules:
         check_rest_api_is_private: PASS
   ```

1. `--verbose` 플래그를 사용하여 터미널에서 `test` 명령을 실행하여 평가 결과를 검사합니다. 상세 컨텍스트는 평가를 이해하는 데 유용합니다. 이 경우 네 번째 테스트가 `PASS` 결과로 성공한 이유에 대한 자세한 정보를 제공합니다.

   ```
   cfn-guard test --rules-file api_gateway_private.guard --test-data api_gateway_private_tests.yaml \
     --verbose
   ```

   다음은 해당 실행의 출력입니다.

   ```
   Test Case #1
   Name: "MyTest4"
     PASS Rules:
       check_rest_api_is_private: Expected = PASS, Evaluated = PASS
   Rule(check_rest_api_is_private, PASS)
       |  Message: DEFAULT MESSAGE(PASS)
       Condition(check_rest_api_is_private, PASS)
           |  Message: DEFAULT MESSAGE(PASS)
           Clause(Clause(Location[file:api_gateway_private.guard, line:20, column:37], Check: %api_gws NOT EMPTY ), PASS)
               |  From: Map((Path("/Resources/apiGw"), MapValue { keys: [String((Path("/Resources/apiGw/Type"), "Type")), String((Path("/Resources/apiGw/Properties"), "Properties"))], values: {"Type": String((Path("/Resources/apiGw/Type"), "AWS::ApiGateway::RestApi")), "Properties": Map((Path("/Resources/apiGw/Properties"), MapValue { keys: [String((Path("/Resources/apiGw/Properties/EndpointConfiguration"), "EndpointConfiguration"))], values: {"EndpointConfiguration": Map((Path("/Resources/apiGw/Properties/EndpointConfiguration"), MapValue { keys: [String((Path("/Resources/apiGw/Properties/EndpointConfiguration/Types"), "Types"))], values: {"Types": String((Path("/Resources/apiGw/Properties/EndpointConfiguration/Types"), "PRIVATE"))} }))} }))} }))
               |  Message: (DEFAULT: NO_MESSAGE)
       Conjunction(cfn_guard::rules::exprs::GuardClause, PASS)
           |  Message: DEFAULT MESSAGE(PASS)
           Clause(Clause(Location[file:api_gateway_private.guard, line:22, column:5], Check: Properties.EndpointConfiguration.Types[*]  EQUALS String("PRIVATE")), PASS)
               |  Message: (DEFAULT: NO_MESSAGE)
   ```

   출력의 주요 관측값은 검사 통과를 `Clause(Location[file:api_gateway_private.guard, line:22, column:5], Check: Properties.EndpointConfiguration.Types[*] EQUALS String("PRIVATE")), PASS)`나타내는 줄입니다. 이 예제에서는가 배열일 것으로 `Types` 예상되지만 단일 값이 제공된 사례도 보여주었습니다. 이 경우 Guard는 계속 평가하여 올바른 결과를 제공했습니다.

1. `EndpointConfiguration` 속성이 지정된 `AWS::ApiGateway::RestApi` 리소스의 단위 테스트 파일에 네 번째 테스트 사례와 같은 테스트 사례를 추가합니다. 테스트 케이스는 통과 대신 실패합니다. 다음은 업데이트된 단위 테스트 파일입니다.

   ```
   ---
   #- name: MyTest1
   #  input: {}
   #  expectations:
   #    rules:
   #      check_rest_api_is_private_and_has_access: SKIP
   #- name: MyTest2
   #  input:
   #     Resources: {}
   #  expectations:
   #    rules:
   #      check_rest_api_is_private_and_has_access: SKIP
   #- name: MyTest3
   #  input:
   #    Resources: 
   #      apiGw:
   #        Type: AWS::ApiGateway::RestApi
   #  expectations:
   #    rules:
   #      check_rest_api_is_private_and_has_access: FAIL
   #- name: MyTest4
   #  input:
   #    Resources: 
   #      apiGw:
   #        Type: AWS::ApiGateway::RestApi
   #        Properties:
   #          EndpointConfiguration:
   #            Types: "PRIVATE"
   #  expectations:
   #    rules:
   #      check_rest_api_is_private: PASS
   - name: MyTest5
     input:
       Resources: 
         apiGw:
           Type: AWS::ApiGateway::RestApi
           Properties:
             EndpointConfiguration:
               Types: [PRIVATE, REGIONAL]
     expectations:
       rules:
         check_rest_api_is_private: FAIL
   ```

1. `--verbose` 플래그를 사용하여 업데이트된 단위 테스트 파일로 `test` 명령을 실행합니다.

   ```
   cfn-guard test --rules-file api_gateway_private.guard --test-data api_gateway_private_tests.yaml \
    --verbose
   ```

   는에 대해 지정`EndpointConfiguration`되지만 `REGIONAL`는 예상되지 않기 때문에 결과는 예상`FAIL`대로입니다.

   ```
   Test Case #1
   Name: "MyTest5"
     PASS Rules: 
       check_rest_api_is_private: Expected = FAIL, Evaluated = FAIL
   Rule(check_rest_api_is_private, FAIL)
       |  Message: DEFAULT MESSAGE(FAIL)
       Condition(check_rest_api_is_private, PASS) 
           |  Message: DEFAULT MESSAGE(PASS)
           Clause(Clause(Location[file:api_gateway_private.guard, line:20, column:37], Check: %api_gws NOT EMPTY ), PASS)
               |  From: Map((Path("/Resources/apiGw"), MapValue { keys: [String((Path("/Resources/apiGw/Type"), "Type")), String((Path("/Resources/apiGw/Properties"), "Properties"))], values: {"Type": String((Path("/Resources/apiGw/Type"), "AWS::ApiGateway::RestApi")), "Properties": Map((Path("/Resources/apiGw/Properties"), MapValue { keys: [String((Path("/Resources/apiGw/Properties/EndpointConfiguration"), "EndpointConfiguration"))], values: {"EndpointConfiguration": Map((Path("/Resources/apiGw/Properties/EndpointConfiguration"), MapValue { keys: [String((Path("/Resources/apiGw/Properties/EndpointConfiguration/Types"), "Types"))], values: {"Types": List((Path("/Resources/apiGw/Properties/EndpointConfiguration/Types"), [String((Path("/Resources/apiGw/Properties/EndpointConfiguration/Types/0"), "PRIVATE")), String((Path("/Resources/apiGw/Properties/EndpointConfiguration/Types/1"), "REGIONAL"))]))} }))} }))} }))
               |  Message: DEFAULT MESSAGE(PASS)
       BlockClause(Block[Location[file:api_gateway_private.guard, line:21, column:3]], FAIL)
           |  Message: DEFAULT MESSAGE(FAIL)
           Conjunction(cfn_guard::rules::exprs::GuardClause, FAIL)
               |  Message: DEFAULT MESSAGE(FAIL)
               Clause(Clause(Location[file:api_gateway_private.guard, line:22, column:5], Check: Properties.EndpointConfiguration.Types[*]  EQUALS String("PRIVATE")), FAIL)
                   |  From: String((Path("/Resources/apiGw/Properties/EndpointConfiguration/Types/1"), "REGIONAL"))
                   |  To: String((Path("api_gateway_private.guard/22/5/Clause/"), "PRIVATE"))
                   |  Message: (DEFAULT: NO_MESSAGE)
   ```

   `test` 명령의 상세 출력은 규칙 파일의 구조를 따릅니다. 규칙 파일의 모든 블록은 상세 출력의 블록입니다. 최상위 블록은 각 규칙입니다. 규칙에 대한 `when` 조건이 있는 경우 형제 조건 블록에 표시됩니다. 다음 예제에서는 조건을 `%api_gws !empty` 테스트하고 통과합니다.

   ```
   rule check_rest_api_is_private when %api_gws !empty {
   ```

   조건이 통과되면 규칙 절을 테스트합니다.

   ```
   %api_gws {
       Properties.EndpointConfiguration.Types[*] == "PRIVATE"                      
   }
   ```

   `%api_gws`는 출력의 `BlockClause` 레벨(line:21)에 해당하는 블록 규칙입니다. 규칙 절은 연결(AND) 절 집합이며, 여기서 각 연결 절은 분리(`OR`s) 집합입니다. 이 연결에는 단일 절인가 있습니다`Properties.EndpointConfiguration.Types[*] == "PRIVATE"`. 따라서 상세 출력에는 단일 절이 표시됩니다. 경로는 입력의 어떤 값이 비교되는지 `/Resources/apiGw/Properties/EndpointConfiguration/Types/1` 보여줍니다.이 경우는 1로 인`Types`덱싱된의 요소입니다.

에서는이 섹션의 예제를 사용하여 `validate` 명령을 사용하여 규칙에 대해 입력 데이터를 평가할 [Guard 규칙에 대한 입력 데이터 검증](validating-rules.md)수 있습니다.