Example #1
0
def create_user(client):
    # type: (Client) -> None

    # {code_example|start}
    # Create a user
    request = {
        'email': '*****@*****.**',
        'password': '******',
        'full_name': 'New User',
        'short_name': 'newbie'
    }
    result = client.create_user(request)
    # {code_example|end}

    validate_against_openapi_schema(result, '/users', 'post', '200')

    # Test "Email already used error"
    result = client.create_user(request)

    validate_against_openapi_schema(result, '/users', 'post', '400')
Example #2
0
def update_subscription_settings(client: Client) -> None:

    # {code_example|start}
    # Update the user's subscription in stream #1 to pin it to the top of the
    # stream list; and in stream #3 to have the hex color "f00"
    request = [{
        'stream_id': 1,
        'property': 'pin_to_top',
        'value': True
    }, {
        'stream_id': 3,
        'property': 'color',
        'value': 'f00'
    }]
    result = client.update_subscription_settings(request)
    # {code_example|end}

    validate_against_openapi_schema(result,
                                    '/users/me/subscriptions/properties',
                                    'POST', '200')
def upload_file(client: Client) -> None:
    path_to_file = os.path.join(ZULIP_DIR, "zerver", "tests", "images", "img.jpg")

    # {code_example|start}
    # Upload a file
    with open(path_to_file, "rb") as fp:
        result = client.upload_file(fp)

    # Share the file by including it in a message.
    client.send_message(
        {
            "type": "stream",
            "to": "Denmark",
            "topic": "Castle",
            "content": "Check out [this picture]({}) of my castle!".format(result["uri"]),
        }
    )
    # {code_example|end}

    validate_against_openapi_schema(result, "/user_uploads", "post", "200")
def check_messages_match_narrow(client: Client) -> None:
    message = {"type": "stream", "to": "Verona", "topic": "test_topic", "content": "http://foo.com"}
    msg_ids = []
    response = client.send_message(message)
    msg_ids.append(response["id"])
    message["content"] = "no link here"
    response = client.send_message(message)
    msg_ids.append(response["id"])

    # {code_example|start}
    # Check which messages within an array match a narrow.
    request = {
        "msg_ids": msg_ids,
        "narrow": [{"operator": "has", "operand": "link"}],
    }

    result = client.call_endpoint(url="messages/matches_narrow", method="GET", request=request)
    # {code_example|end}

    validate_against_openapi_schema(result, "/messages/matches_narrow", "get", "200")
Example #5
0
    def validate_api_response_openapi(self, url: str, method: str,
                                      result: HttpResponse) -> None:
        """
        Validates all API responses received by this test against Zulip's API documentation,
        declared in zerver/openapi/zulip.yaml.  This powerful test lets us use Zulip's
        extensive test coverage of corner cases in the API to ensure that we've properly
        documented those corner cases.
        """
        if not (url.startswith("/json") or url.startswith("/api/v1")):
            return

        try:
            content = ujson.loads(result.content)
        except ValueError:
            return
        url = re.sub(r"\?.*", "", url)
        validate_against_openapi_schema(
            content,
            url.replace("/json/", "/").replace("/api/v1/", "/"), method,
            str(result.status_code))
Example #6
0
def update_user(client: Client) -> None:
    ensure_users([8, 10], ["cordelia", "hamlet"])

    # {code_example|start}
    # Change a user's full name.
    user_id = 10
    result = client.update_user_by_id(user_id, full_name="New Name")
    # {code_example|end}
    validate_against_openapi_schema(result, "/users/{user_id}", "patch", "200")

    # {code_example|start}
    # Change value of the custom profile field with ID 9.
    user_id = 8
    result = client.update_user_by_id(user_id,
                                      profile_data=[{
                                          "id": 9,
                                          "value": "some data"
                                      }])
    # {code_example|end}
    validate_against_openapi_schema(result, "/users/{user_id}", "patch", "400")
Example #7
0
def get_streams(client):
    # type: (Client) -> None

    # {code_example|start}
    # Get all streams that the user has access to
    result = client.get_streams()
    # {code_example|end}

    validate_against_openapi_schema(result, '/streams', 'get', '200')
    streams = [s for s in result['streams'] if s['name'] == 'new stream']
    assert streams[0]['description'] == 'New stream for testing'

    # {code_example|start}
    # You may pass in one or more of the query parameters mentioned above
    # as keyword arguments, like so:
    result = client.get_streams(include_public=False)
    # {code_example|end}

    validate_against_openapi_schema(result, '/streams', 'get', '200')
    assert len(result['streams']) == 4
Example #8
0
def update_message(client: Client, message_id: int) -> None:

    assert int(message_id)

    # {code_example|start}
    # Edit a message
    # (make sure that message_id below is set to the ID of the
    # message you wish to update)
    request = {"message_id": message_id, "content": "New content"}
    result = client.update_message(request)
    # {code_example|end}

    validate_against_openapi_schema(result, '/messages/{message_id}', 'patch',
                                    '200')

    # test it was actually updated
    url = 'messages/' + str(message_id)
    result = client.call_endpoint(url=url, method='GET')
    assert result['result'] == 'success'
    assert result['raw_content'] == request['content']
Example #9
0
def upload_file(client: Client) -> None:
    path_to_file = os.path.join(ZULIP_DIR, 'zerver', 'tests', 'images', 'img.jpg')

    # {code_example|start}
    # Upload a file
    with open(path_to_file, 'rb') as fp:
        result = client.call_endpoint(
            'user_uploads',
            method='POST',
            files=[fp],
        )

    client.send_message({
        "type": "stream",
        "to": "Denmark",
        "topic": "Castle",
        "content": "Check out [this picture]({}) of my castle!".format(result['uri']),
    })
    # {code_example|end}

    validate_against_openapi_schema(result, '/user_uploads', 'post', '200')
Example #10
0
def update_subscription_settings(client: Client) -> None:

    # {code_example|start}
    # Update the user's subscription in stream #1 to pin it to the top of the
    # stream list; and in stream #3 to have the hex color "f00"
    request = [
        {
            "stream_id": 1,
            "property": "pin_to_top",
            "value": True,
        },
        {
            "stream_id": 3,
            "property": "color",
            "value": "#f00f00",
        },
    ]
    result = client.update_subscription_settings(request)
    # {code_example|end}

    validate_against_openapi_schema(result, "/users/me/subscriptions/properties", "POST", "200")
Example #11
0
def get_members(client: Client) -> None:

    # {code_example|start}
    # Get all users in the realm
    result = client.get_members()
    # {code_example|end}

    validate_against_openapi_schema(result, '/users', 'get', '200')

    members = [m for m in result['members'] if m['email'] == '*****@*****.**']
    assert len(members) == 1
    newbie = members[0]
    assert not newbie['is_admin']
    assert newbie['full_name'] == 'New User'

    # {code_example|start}
    # You may pass the `client_gravatar` query parameter as follows:
    result = client.get_members({'client_gravatar': True})
    # {code_example|end}

    validate_against_openapi_schema(result, '/users', 'get', '200')
    assert result['members'][0]['avatar_url'] is None

    # {code_example|start}
    # You may pass the `include_custom_profile_fields` query parameter as follows:
    result = client.get_members({'include_custom_profile_fields': True})
    # {code_example|end}

    validate_against_openapi_schema(result, '/users', 'get', '200')
    for member in result['members']:
        if member["is_bot"]:
            assert member.get('profile_data', None) is None
        else:
            assert member.get('profile_data', None) is not None
Example #12
0
def get_members(client: Client) -> None:

    # {code_example|start}
    # Get all users in the realm
    result = client.get_members()
    # {code_example|end}

    validate_against_openapi_schema(result, "/users", "get", "200")

    members = [m for m in result["members"] if m["email"] == "*****@*****.**"]
    assert len(members) == 1
    newbie = members[0]
    assert not newbie["is_admin"]
    assert newbie["full_name"] == "New User"

    # {code_example|start}
    # You may pass the `client_gravatar` query parameter as follows:
    result = client.get_members({"client_gravatar": True})
    # {code_example|end}

    validate_against_openapi_schema(result, "/users", "get", "200")
    assert result["members"][0]["avatar_url"] is None

    # {code_example|start}
    # You may pass the `include_custom_profile_fields` query parameter as follows:
    result = client.get_members({"include_custom_profile_fields": True})
    # {code_example|end}

    validate_against_openapi_schema(result, "/users", "get", "200")
    for member in result["members"]:
        if member["is_bot"]:
            assert member.get("profile_data", None) is None
        else:
            assert member.get("profile_data", None) is not None
Example #13
0
def test_js_bindings(client: Client) -> None:
    os.environ['ZULIP_USERNAME'] = client.email
    os.environ['ZULIP_API_KEY'] = client.api_key
    os.environ['ZULIP_REALM'] = client.base_url[:-5]

    output = subprocess.check_output(
        args=[
            'node', '--unhandled-rejections=strict',
            'zerver/openapi/javascript_examples.js'
        ],
        universal_newlines=True,
    )
    endpoint_responses = json.loads(output)

    for response_data in endpoint_responses:
        print(f"Testing javascript example: {response_data['name']} ...")
        validate_against_openapi_schema(response_data['result'],
                                        response_data['endpoint'],
                                        response_data['method'],
                                        response_data['status_code'])

    print('JavaScript examples validated.')
Example #14
0
def update_message_flags(client):
    # type: (Client) -> None

    # Send a few test messages
    request = {
        "type": "stream",
        "to": "Denmark",
        "topic": "Castle",
        "content": "I come not, friends, to steal away your hearts."
    }  # type: Dict[str, Any]
    message_ids = []
    for i in range(0, 3):
        message_ids.append(client.send_message(request)['id'])

    # {code_example|start}
    # Add the "read" flag to the messages with IDs in "message_ids"
    request = {
        'messages': message_ids,
        'op': 'add',
        'flag': 'read'
    }
    result = client.update_message_flags(request)
    # {code_example|end}

    validate_against_openapi_schema(result, '/messages/flags', 'post',
                                    '200')

    # {code_example|start}
    # Remove the "starred" flag from the messages with IDs in "message_ids"
    request = {
        'messages': message_ids,
        'op': 'remove',
        'flag': 'starred'
    }
    result = client.update_message_flags(request)
    # {code_example|end}

    validate_against_openapi_schema(result, '/messages/flags', 'post',
                                    '200')
Example #15
0
def toggle_mute_topic(client: Client) -> None:

    # Send a test message
    message = {
        "type": "stream",
        "to": "Denmark",
        "topic": "boat party",
    }
    client.call_endpoint(
        url="messages",
        method="POST",
        request=message,
    )

    # {code_example|start}
    # Mute the topic "boat party" in the stream "Denmark"
    request = {
        "stream": "Denmark",
        "topic": "boat party",
        "op": "add",
    }
    result = client.mute_topic(request)
    # {code_example|end}

    validate_against_openapi_schema(result, "/users/me/subscriptions/muted_topics", "patch", "200")

    # {code_example|start}
    # Unmute the topic "boat party" in the stream "Denmark"
    request = {
        "stream": "Denmark",
        "topic": "boat party",
        "op": "remove",
    }

    result = client.mute_topic(request)
    # {code_example|end}

    validate_against_openapi_schema(result, "/users/me/subscriptions/muted_topics", "patch", "200")
Example #16
0
def test_authorization_errors_fatal(client: Client,
                                    nonadmin_client: Client) -> None:
    client.add_subscriptions(streams=[
        {
            'name': 'private_stream'
        },
    ], )

    stream_id = client.get_stream_id('private_stream')['stream_id']
    client.call_endpoint(
        f'streams/{stream_id}',
        method='PATCH',
        request={'is_private': True},
    )

    result = nonadmin_client.add_subscriptions(
        streams=[
            {
                'name': 'private_stream'
            },
        ],
        authorization_errors_fatal=False,
    )

    validate_against_openapi_schema(result, '/users/me/subscriptions', 'post',
                                    '400_0')

    result = nonadmin_client.add_subscriptions(
        streams=[
            {
                'name': 'private_stream'
            },
        ],
        authorization_errors_fatal=True,
    )

    validate_against_openapi_schema(result, '/users/me/subscriptions', 'post',
                                    '400_1')
Example #17
0
def test_authorization_errors_fatal(client: Client,
                                    nonadmin_client: Client) -> None:
    client.add_subscriptions(streams=[
        {
            "name": "private_stream"
        },
    ], )

    stream_id = client.get_stream_id("private_stream")["stream_id"]
    client.call_endpoint(
        f"streams/{stream_id}",
        method="PATCH",
        request={"is_private": True},
    )

    result = nonadmin_client.add_subscriptions(
        streams=[
            {
                "name": "private_stream"
            },
        ],
        authorization_errors_fatal=False,
    )

    validate_against_openapi_schema(result, "/users/me/subscriptions", "post",
                                    "400_0")

    result = nonadmin_client.add_subscriptions(
        streams=[
            {
                "name": "private_stream"
            },
        ],
        authorization_errors_fatal=True,
    )

    validate_against_openapi_schema(result, "/users/me/subscriptions", "post",
                                    "400_1")
Example #18
0
 def test_attributes(self) -> None:
     """
     Checks:
     * All endpoints have `operationId` and `tag` attributes.
     * All example responses match their schema.
     * That no opaque object exists.
     """
     EXCLUDE = ["/real-time", "/register", "/events"]
     VALID_TAGS = [
         "users", "server_and_organizations", "authentication",
         "real_time_events", "streams", "messages", "users", "webhooks"
     ]
     openapi_spec = OpenAPISpec(OPENAPI_SPEC_PATH).spec()["paths"]
     for path in openapi_spec:
         if path in EXCLUDE:
             continue
         for method in openapi_spec[path]:
             # Check if every file has an operationId
             assert ("operationId" in openapi_spec[path][method])
             assert ("tags" in openapi_spec[path][method])
             tag = openapi_spec[path][method]["tags"][0]
             assert (tag in VALID_TAGS)
             for response in openapi_spec[path][method]['responses']:
                 response_schema = (
                     openapi_spec[path][method]['responses'][response]
                     ['content']['application/json']['schema'])
                 if 'oneOf' in response_schema:
                     cnt = 0
                     for entry in response_schema['oneOf']:
                         validate_schema(entry)
                         assert (validate_against_openapi_schema(
                             entry['example'], path, method,
                             response + '_' + str(cnt)))
                         cnt += 1
                     continue
                 validate_schema(response_schema)
                 assert (validate_against_openapi_schema(
                     response_schema['example'], path, method, response))
Example #19
0
def update_message_flags(client: Client) -> None:

    # Send a few test messages
    request: Dict[str, Any] = {
        "type": "stream",
        "to": "Denmark",
        "topic": "Castle",
        "content": "I come not, friends, to steal away your hearts.",
    }
    message_ids = []
    for i in range(0, 3):
        message_ids.append(client.send_message(request)["id"])

    # {code_example|start}
    # Add the "read" flag to the messages with IDs in "message_ids"
    request = {
        "messages": message_ids,
        "op": "add",
        "flag": "read",
    }
    result = client.update_message_flags(request)
    # {code_example|end}

    validate_against_openapi_schema(result, "/messages/flags", "post", "200")

    # {code_example|start}
    # Remove the "starred" flag from the messages with IDs in "message_ids"
    request = {
        "messages": message_ids,
        "op": "remove",
        "flag": "starred",
    }
    result = client.update_message_flags(request)
    # {code_example|end}

    validate_against_openapi_schema(result, "/messages/flags", "post", "200")
Example #20
0
def update_user(client: Client) -> None:

    # {code_example|start}
    # Change a user's full name.
    user_id = 10
    full_name = "New Name"
    url = 'users/' + str(user_id)
    result = client.call_endpoint(url=url,
                                  method='PATCH',
                                  request={'full_name': json.dumps(full_name)})
    # {code_example|end}
    validate_against_openapi_schema(result, '/users/{user_id}', 'patch', '200')

    # {code_example|start}
    # Change value of the custom profile field with ID 9.
    user_id = 8
    profile_data = [{'id': 9, 'value': 'some data'}]
    url = 'users/' + str(user_id)
    result = client.call_endpoint(
        url=url,
        method='PATCH',
        request={'profile_data': json.dumps(profile_data)})
    # {code_example|end}
    validate_against_openapi_schema(result, '/users/{user_id}', 'patch', '400')
Example #21
0
def get_single_user(client):
    # type: (Client) -> None

    # {code_example|start}
    # Fetch details on a user given a user ID
    user_id = 8
    url = 'users/' + str(user_id)
    result = client.call_endpoint(
        url=url,
        method='GET'
    )
    # {code_example|end}
    validate_against_openapi_schema(result, '/users/{user_id}', 'get', '200')

    # {code_example|start}
    # If you'd like data on custom profile fields, you can request them as follows:
    result = client.call_endpoint(
        url=url,
        method='GET',
        request={'include_custom_profile_fields': True}
    )
    # {code_example|end}

    validate_against_openapi_schema(result, '/users/{user_id}', 'get', '200')
Example #22
0
def get_messages(client: Client) -> None:

    # {code_example|start}
    # Get the 100 last messages sent by "*****@*****.**" to the stream "Verona"
    request: Dict[str, Any] = {
        'anchor':
        'newest',
        'num_before':
        100,
        'num_after':
        0,
        'narrow': [{
            'operator': 'sender',
            'operand': '*****@*****.**'
        }, {
            'operator': 'stream',
            'operand': 'Verona'
        }],
    }
    result = client.get_messages(request)
    # {code_example|end}

    validate_against_openapi_schema(result, '/messages', 'get', '200')
    assert len(result['messages']) <= request['num_before']
Example #23
0
def remove_subscriptions(client: Client) -> None:

    # {code_example|start}
    # Unsubscribe from the stream "new stream"
    result = client.remove_subscriptions(['new stream'])
    # {code_example|end}

    validate_against_openapi_schema(result, '/users/me/subscriptions',
                                    'delete', '200')

    # test it was actually removed
    result = client.list_subscriptions()
    assert result['result'] == 'success'
    streams = [s for s in result['subscriptions'] if s['name'] == 'new stream']
    assert len(streams) == 0

    # {code_example|start}
    # Unsubscribe another user from the stream "new stream"
    result = client.remove_subscriptions(['new stream'],
                                         principals=['*****@*****.**'])
    # {code_example|end}

    validate_against_openapi_schema(result, '/users/me/subscriptions',
                                    'delete', '200')
Example #24
0
def test_js_bindings(client: Client) -> None:
    os.environ["ZULIP_USERNAME"] = client.email
    os.environ["ZULIP_API_KEY"] = client.api_key
    os.environ["ZULIP_REALM"] = client.base_url[:-5]

    output = subprocess.check_output(
        args=[
            "node", "--unhandled-rejections=strict",
            "zerver/openapi/javascript_examples.js"
        ],
        text=True,
    )
    endpoint_responses = json.loads(output)

    for response_data in endpoint_responses:
        print(f"Testing javascript example: {response_data['name']} ...")
        validate_against_openapi_schema(
            response_data["result"],
            response_data["endpoint"],
            response_data["method"],
            response_data["status_code"],
        )

    print("JavaScript examples validated.")
Example #25
0
 def validate_api_response_openapi(
     self,
     url: str,
     method: str,
     result: HttpResponse,
     data: Union[str, bytes, Dict[str, Any]],
     http_headers: Dict[str, Any],
     intentionally_undocumented: bool = False,
 ) -> None:
     """
     Validates all API responses received by this test against Zulip's API documentation,
     declared in zerver/openapi/zulip.yaml.  This powerful test lets us use Zulip's
     extensive test coverage of corner cases in the API to ensure that we've properly
     documented those corner cases.
     """
     if not (url.startswith("/json") or url.startswith("/api/v1")):
         return
     try:
         content = orjson.loads(result.content)
     except orjson.JSONDecodeError:
         return
     json_url = False
     if url.startswith("/json"):
         json_url = True
     url, query_data = self.extract_api_suffix_url(url)
     if len(query_data) != 0:
         # In some cases the query parameters are defined in the URL itself. In such cases
         # The `data` argument of our function is not used. Hence get `data` argument
         # from url.
         data = query_data
     response_validated = validate_against_openapi_schema(
         content, url, method, str(result.status_code)
     )
     if response_validated:
         validate_request(
             url,
             method,
             data,
             http_headers,
             json_url,
             str(result.status_code),
             intentionally_undocumented=intentionally_undocumented,
         )
Example #26
0
    def test_validate_against_openapi_schema(self) -> None:
        with self.assertRaises(
                ValidationError,
                msg=
                "Additional properties are not allowed ('foo' was unexpected)"
        ):
            bad_content: Dict[str, object] = {
                "msg": "",
                "result": "success",
                "foo": "bar",
            }
            validate_against_openapi_schema(bad_content, TEST_ENDPOINT,
                                            TEST_METHOD, TEST_RESPONSE_SUCCESS)

        with self.assertRaises(ValidationError,
                               msg=("42 is not of type string")):
            bad_content = {
                "msg": 42,
                "result": "success",
            }
            validate_against_openapi_schema(bad_content, TEST_ENDPOINT,
                                            TEST_METHOD, TEST_RESPONSE_SUCCESS)

        with self.assertRaises(ValidationError,
                               msg='Expected to find the "msg" required key'):
            bad_content = {
                "result": "success",
            }
            validate_against_openapi_schema(bad_content, TEST_ENDPOINT,
                                            TEST_METHOD, TEST_RESPONSE_SUCCESS)

        # No exceptions should be raised here.
        good_content = {
            "msg": "",
            "result": "success",
        }
        validate_against_openapi_schema(good_content, TEST_ENDPOINT,
                                        TEST_METHOD, TEST_RESPONSE_SUCCESS)

        # Overwrite the exception list with a mocked one
        test_dict: Dict[str, Any] = {}

        # Check that validate_against_openapi_schema correctly
        # descends into 'deep' objects and arrays.  Test 1 should
        # pass, Test 2 has a 'deep' extraneous key and Test 3 has a
        # 'deep' opaque object. Also the parameters are a heterogeneous
        # mix of arrays and objects to verify that our descent logic
        # correctly gets to the the deeply nested objects.
        with open(
                os.path.join(os.path.dirname(OPENAPI_SPEC_PATH),
                             "testing.yaml")) as test_file:
            test_dict = yaml.safe_load(test_file)
        openapi_spec.openapi()["paths"]["testing"] = test_dict
        try:
            validate_against_openapi_schema(
                (test_dict["test1"]["responses"]["200"]["content"]
                 ["application/json"]["example"]),
                "testing",
                "test1",
                "200",
            )
            with self.assertRaises(
                    ValidationError,
                    msg='Extraneous key "str4" in response\'s content'):
                validate_against_openapi_schema(
                    (test_dict["test2"]["responses"]["200"]["content"]
                     ["application/json"]["example"]),
                    "testing",
                    "test2",
                    "200",
                )
            with self.assertRaises(SchemaError, msg='Opaque object "obj"'):
                # Checks for opaque objects
                validate_schema(test_dict["test3"]["responses"]["200"]
                                ["content"]["application/json"]["schema"])
        finally:
            openapi_spec.openapi()["paths"].pop("testing", None)
Example #27
0
def test_user_not_authorized_error(nonadmin_client: Client) -> None:
    result = nonadmin_client.get_streams(include_all_active=True)

    validate_against_openapi_schema(result, "/rest-error-handling", "post",
                                    "400_2")
Example #28
0
def test_missing_request_argument(client: Client) -> None:
    result = client.render_message({})

    validate_against_openapi_schema(result, "/rest-error-handling", "post",
                                    "400_1")
Example #29
0
def test_invalid_stream_error(client: Client) -> None:
    result = client.get_stream_id("nonexistent")

    validate_against_openapi_schema(result, "/get_stream_id", "get", "400")
Example #30
0
def test_invalid_api_key(client_with_invalid_key: Client) -> None:
    result = client_with_invalid_key.list_subscriptions()
    validate_against_openapi_schema(result, "/rest-error-handling", "post",
                                    "400_0")