Rudimentary support for IVS (#6894)
This commit is contained in:
parent
b2455e4211
commit
5cd288b42c
@ -4105,6 +4105,42 @@
|
|||||||
- [X] update_thing_shadow
|
- [X] update_thing_shadow
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
## ivs
|
||||||
|
<details>
|
||||||
|
<summary>20% implemented</summary>
|
||||||
|
|
||||||
|
- [X] batch_get_channel
|
||||||
|
- [ ] batch_get_stream_key
|
||||||
|
- [ ] batch_start_viewer_session_revocation
|
||||||
|
- [X] create_channel
|
||||||
|
- [ ] create_recording_configuration
|
||||||
|
- [ ] create_stream_key
|
||||||
|
- [X] delete_channel
|
||||||
|
- [ ] delete_playback_key_pair
|
||||||
|
- [ ] delete_recording_configuration
|
||||||
|
- [ ] delete_stream_key
|
||||||
|
- [X] get_channel
|
||||||
|
- [ ] get_playback_key_pair
|
||||||
|
- [ ] get_recording_configuration
|
||||||
|
- [ ] get_stream
|
||||||
|
- [ ] get_stream_key
|
||||||
|
- [ ] get_stream_session
|
||||||
|
- [ ] import_playback_key_pair
|
||||||
|
- [X] list_channels
|
||||||
|
- [ ] list_playback_key_pairs
|
||||||
|
- [ ] list_recording_configurations
|
||||||
|
- [ ] list_stream_keys
|
||||||
|
- [ ] list_stream_sessions
|
||||||
|
- [ ] list_streams
|
||||||
|
- [ ] list_tags_for_resource
|
||||||
|
- [ ] put_metadata
|
||||||
|
- [ ] start_viewer_session_revocation
|
||||||
|
- [ ] stop_stream
|
||||||
|
- [ ] tag_resource
|
||||||
|
- [ ] untag_resource
|
||||||
|
- [X] update_channel
|
||||||
|
</details>
|
||||||
|
|
||||||
## kinesis
|
## kinesis
|
||||||
<details>
|
<details>
|
||||||
<summary>93% implemented</summary>
|
<summary>93% implemented</summary>
|
||||||
@ -7481,7 +7517,6 @@
|
|||||||
- iotthingsgraph
|
- iotthingsgraph
|
||||||
- iottwinmaker
|
- iottwinmaker
|
||||||
- iotwireless
|
- iotwireless
|
||||||
- ivs
|
|
||||||
- ivs-realtime
|
- ivs-realtime
|
||||||
- ivschat
|
- ivschat
|
||||||
- kafka
|
- kafka
|
||||||
|
60
docs/docs/services/ivs.rst
Normal file
60
docs/docs/services/ivs.rst
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
.. _implementedservice_ivs:
|
||||||
|
|
||||||
|
.. |start-h3| raw:: html
|
||||||
|
|
||||||
|
<h3>
|
||||||
|
|
||||||
|
.. |end-h3| raw:: html
|
||||||
|
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
===
|
||||||
|
ivs
|
||||||
|
===
|
||||||
|
|
||||||
|
.. autoclass:: moto.ivs.models.IVSBackend
|
||||||
|
|
||||||
|
|start-h3| Example usage |end-h3|
|
||||||
|
|
||||||
|
.. sourcecode:: python
|
||||||
|
|
||||||
|
@mock_ivs
|
||||||
|
def test_ivs_behaviour:
|
||||||
|
boto3.client("ivs")
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|start-h3| Implemented features for this service |end-h3|
|
||||||
|
|
||||||
|
- [X] batch_get_channel
|
||||||
|
- [ ] batch_get_stream_key
|
||||||
|
- [ ] batch_start_viewer_session_revocation
|
||||||
|
- [X] create_channel
|
||||||
|
- [ ] create_recording_configuration
|
||||||
|
- [ ] create_stream_key
|
||||||
|
- [X] delete_channel
|
||||||
|
- [ ] delete_playback_key_pair
|
||||||
|
- [ ] delete_recording_configuration
|
||||||
|
- [ ] delete_stream_key
|
||||||
|
- [X] get_channel
|
||||||
|
- [ ] get_playback_key_pair
|
||||||
|
- [ ] get_recording_configuration
|
||||||
|
- [ ] get_stream
|
||||||
|
- [ ] get_stream_key
|
||||||
|
- [ ] get_stream_session
|
||||||
|
- [ ] import_playback_key_pair
|
||||||
|
- [X] list_channels
|
||||||
|
- [ ] list_playback_key_pairs
|
||||||
|
- [ ] list_recording_configurations
|
||||||
|
- [ ] list_stream_keys
|
||||||
|
- [ ] list_stream_sessions
|
||||||
|
- [ ] list_streams
|
||||||
|
- [ ] list_tags_for_resource
|
||||||
|
- [ ] put_metadata
|
||||||
|
- [ ] start_viewer_session_revocation
|
||||||
|
- [ ] stop_stream
|
||||||
|
- [ ] tag_resource
|
||||||
|
- [ ] untag_resource
|
||||||
|
- [X] update_channel
|
||||||
|
|
@ -106,6 +106,7 @@ mock_iam = lazy_load(".iam", "mock_iam")
|
|||||||
mock_identitystore = lazy_load(".identitystore", "mock_identitystore")
|
mock_identitystore = lazy_load(".identitystore", "mock_identitystore")
|
||||||
mock_iot = lazy_load(".iot", "mock_iot")
|
mock_iot = lazy_load(".iot", "mock_iot")
|
||||||
mock_iotdata = lazy_load(".iotdata", "mock_iotdata", boto3_name="iot-data")
|
mock_iotdata = lazy_load(".iotdata", "mock_iotdata", boto3_name="iot-data")
|
||||||
|
mock_ivs = lazy_load(".ivs", "mock_ivs", boto3_name="ivs")
|
||||||
mock_kinesis = lazy_load(".kinesis", "mock_kinesis")
|
mock_kinesis = lazy_load(".kinesis", "mock_kinesis")
|
||||||
mock_kinesisvideo = lazy_load(".kinesisvideo", "mock_kinesisvideo")
|
mock_kinesisvideo = lazy_load(".kinesisvideo", "mock_kinesisvideo")
|
||||||
mock_kinesisvideoarchivedmedia = lazy_load(
|
mock_kinesisvideoarchivedmedia = lazy_load(
|
||||||
|
@ -88,6 +88,7 @@ backend_url_patterns = [
|
|||||||
("iot", re.compile("https?://iot\\.(.+)\\.amazonaws\\.com")),
|
("iot", re.compile("https?://iot\\.(.+)\\.amazonaws\\.com")),
|
||||||
("iot-data", re.compile("https?://data\\.iot\\.(.+)\\.amazonaws.com")),
|
("iot-data", re.compile("https?://data\\.iot\\.(.+)\\.amazonaws.com")),
|
||||||
("iot-data", re.compile("https?://data-ats\\.iot\\.(.+)\\.amazonaws.com")),
|
("iot-data", re.compile("https?://data-ats\\.iot\\.(.+)\\.amazonaws.com")),
|
||||||
|
("ivs", re.compile("https?://ivs\\.(.+)\\.amazonaws\\.com")),
|
||||||
("kinesis", re.compile("https?://kinesis\\.(.+)\\.amazonaws\\.com")),
|
("kinesis", re.compile("https?://kinesis\\.(.+)\\.amazonaws\\.com")),
|
||||||
("kinesis", re.compile("https?://(.+)\\.control-kinesis\\.(.+)\\.amazonaws\\.com")),
|
("kinesis", re.compile("https?://(.+)\\.control-kinesis\\.(.+)\\.amazonaws\\.com")),
|
||||||
("kinesis", re.compile("https?://(.+)\\.data-kinesis\\.(.+)\\.amazonaws\\.com")),
|
("kinesis", re.compile("https?://(.+)\\.data-kinesis\\.(.+)\\.amazonaws\\.com")),
|
||||||
|
5
moto/ivs/__init__.py
Normal file
5
moto/ivs/__init__.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
"""ivs module initialization; sets value for base decorator."""
|
||||||
|
from .models import ivs_backends
|
||||||
|
from ..core.models import base_decorator
|
||||||
|
|
||||||
|
mock_ivs = base_decorator(ivs_backends)
|
9
moto/ivs/exceptions.py
Normal file
9
moto/ivs/exceptions.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
"""Exceptions raised by the ivs service."""
|
||||||
|
from moto.core.exceptions import JsonRESTError
|
||||||
|
|
||||||
|
|
||||||
|
class ResourceNotFoundException(JsonRESTError):
|
||||||
|
code = 404
|
||||||
|
|
||||||
|
def __init__(self, message: str):
|
||||||
|
super().__init__("ResourceNotFoundException", message)
|
134
moto/ivs/models.py
Normal file
134
moto/ivs/models.py
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
"""IVSBackend class with methods for supported APIs."""
|
||||||
|
from typing import Optional, Any, List, Dict, Tuple
|
||||||
|
from moto.core import BaseBackend, BackendDict
|
||||||
|
from moto.ivs.exceptions import ResourceNotFoundException
|
||||||
|
from moto.utilities.paginator import paginate
|
||||||
|
from moto.moto_api._internal import mock_random
|
||||||
|
|
||||||
|
|
||||||
|
class IVSBackend(BaseBackend):
|
||||||
|
"""Implementation of IVS APIs."""
|
||||||
|
|
||||||
|
PAGINATION_MODEL = {
|
||||||
|
"list_channels": {
|
||||||
|
"input_token": "next_token",
|
||||||
|
"limit_key": "max_results",
|
||||||
|
"limit_default": 100,
|
||||||
|
"unique_attribute": "arn",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, region_name: str, account_id: str):
|
||||||
|
super().__init__(region_name, account_id)
|
||||||
|
self.channels: List[Dict[str, Any]] = []
|
||||||
|
|
||||||
|
def create_channel(
|
||||||
|
self,
|
||||||
|
authorized: bool,
|
||||||
|
insecure_ingest: bool,
|
||||||
|
latency_mode: str,
|
||||||
|
name: str,
|
||||||
|
preset: str,
|
||||||
|
recording_configuration_arn: str,
|
||||||
|
tags: Dict[str, str],
|
||||||
|
channel_type: str,
|
||||||
|
) -> Tuple[Dict[str, Any], Dict[str, Any]]:
|
||||||
|
channel_id = mock_random.get_random_string(12)
|
||||||
|
channel_arn = (
|
||||||
|
f"arn:aws:ivs:{self.region_name}:{self.account_id}:channel/{channel_id}"
|
||||||
|
)
|
||||||
|
channel = {
|
||||||
|
"arn": channel_arn,
|
||||||
|
"authorized": authorized,
|
||||||
|
"ingestEndpoint": "ingest.example.com",
|
||||||
|
"insecureIngest": insecure_ingest,
|
||||||
|
"latencyMode": latency_mode,
|
||||||
|
"name": name,
|
||||||
|
"playbackUrl": f"https://playback.example.com/{self.region_name}.{self.account_id}.{channel_id}.m3u8",
|
||||||
|
"preset": preset,
|
||||||
|
"recordingConfigurationArn": recording_configuration_arn,
|
||||||
|
"tags": tags,
|
||||||
|
"type": channel_type,
|
||||||
|
}
|
||||||
|
self.channels.append(channel)
|
||||||
|
stream_key_id = mock_random.get_random_string(12)
|
||||||
|
stream_key_arn = f"arn:aws:ivs:{self.region_name}:{self.account_id}:stream-key/{stream_key_id}"
|
||||||
|
stream_key = {
|
||||||
|
"arn": stream_key_arn,
|
||||||
|
"channelArn": channel_arn,
|
||||||
|
"tags": tags,
|
||||||
|
"value": f"sk_{self.region_name}_{mock_random.token_urlsafe(32)}",
|
||||||
|
}
|
||||||
|
return channel, stream_key
|
||||||
|
|
||||||
|
@paginate(pagination_model=PAGINATION_MODEL) # type: ignore[misc]
|
||||||
|
def list_channels(
|
||||||
|
self,
|
||||||
|
filter_by_name: Optional[str],
|
||||||
|
filter_by_recording_configuration_arn: Optional[str],
|
||||||
|
) -> List[Dict[str, Any]]:
|
||||||
|
if filter_by_name is not None:
|
||||||
|
channels = [
|
||||||
|
channel
|
||||||
|
for channel in self.channels
|
||||||
|
if channel["name"] == filter_by_name
|
||||||
|
]
|
||||||
|
elif filter_by_recording_configuration_arn is not None:
|
||||||
|
channels = [
|
||||||
|
channel
|
||||||
|
for channel in self.channels
|
||||||
|
if channel["recordingConfigurationArn"]
|
||||||
|
== filter_by_recording_configuration_arn
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
channels = self.channels
|
||||||
|
return channels
|
||||||
|
|
||||||
|
def _find_channel(self, arn: str) -> Dict[str, Any]:
|
||||||
|
try:
|
||||||
|
return next(channel for channel in self.channels if channel["arn"] == arn)
|
||||||
|
except StopIteration:
|
||||||
|
raise ResourceNotFoundException(f"Resource: {arn} not found")
|
||||||
|
|
||||||
|
def get_channel(self, arn: str) -> Dict[str, Any]:
|
||||||
|
return self._find_channel(arn)
|
||||||
|
|
||||||
|
def batch_get_channel(
|
||||||
|
self, arns: List[str]
|
||||||
|
) -> Tuple[List[Dict[str, Any]], List[Dict[str, str]]]:
|
||||||
|
return [channel for channel in self.channels if channel["arn"] in arns], []
|
||||||
|
|
||||||
|
def update_channel(
|
||||||
|
self,
|
||||||
|
arn: str,
|
||||||
|
authorized: Optional[bool],
|
||||||
|
insecure_ingest: Optional[bool],
|
||||||
|
latency_mode: Optional[str],
|
||||||
|
name: Optional[str],
|
||||||
|
preset: Optional[str],
|
||||||
|
recording_configuration_arn: Optional[str],
|
||||||
|
channel_type: Optional[str],
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
channel = self._find_channel(arn)
|
||||||
|
if authorized is not None:
|
||||||
|
channel["authorized"] = authorized
|
||||||
|
if insecure_ingest is not None:
|
||||||
|
channel["insecureIngest"] = insecure_ingest
|
||||||
|
if latency_mode is not None:
|
||||||
|
channel["latencyMode"] = latency_mode
|
||||||
|
if name is not None:
|
||||||
|
channel["name"] = name
|
||||||
|
if preset is not None:
|
||||||
|
channel["preset"] = preset
|
||||||
|
if recording_configuration_arn is not None:
|
||||||
|
channel["recordingConfigurationArn"] = recording_configuration_arn
|
||||||
|
if channel_type is not None:
|
||||||
|
channel["type"] = channel_type
|
||||||
|
return channel
|
||||||
|
|
||||||
|
def delete_channel(self, arn: str) -> None:
|
||||||
|
self._find_channel(arn)
|
||||||
|
self.channels = [channel for channel in self.channels if channel["arn"] != arn]
|
||||||
|
|
||||||
|
|
||||||
|
ivs_backends = BackendDict(IVSBackend, "ivs")
|
93
moto/ivs/responses.py
Normal file
93
moto/ivs/responses.py
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
"""Handles incoming ivs requests, invokes methods, returns responses."""
|
||||||
|
import json
|
||||||
|
from moto.core.responses import BaseResponse
|
||||||
|
from .models import IVSBackend, ivs_backends
|
||||||
|
|
||||||
|
|
||||||
|
class IVSResponse(BaseResponse):
|
||||||
|
"""Handler for IVS requests and responses."""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
super().__init__(service_name="ivs")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ivs_backend(self) -> IVSBackend:
|
||||||
|
"""Return backend instance specific for this region."""
|
||||||
|
return ivs_backends[self.current_account][self.region]
|
||||||
|
|
||||||
|
def create_channel(self) -> str:
|
||||||
|
authorized = self._get_param("authorized", False)
|
||||||
|
insecure_ingest = self._get_param("insecureIngest", False)
|
||||||
|
latency_mode = self._get_param("latencyMode", "LOW")
|
||||||
|
name = self._get_param("name")
|
||||||
|
preset = self._get_param("preset", "")
|
||||||
|
recording_configuration_arn = self._get_param("recordingConfigurationArn", "")
|
||||||
|
tags = self._get_param("tags", {})
|
||||||
|
channel_type = self._get_param("type", "STANDARD")
|
||||||
|
channel, stream_key = self.ivs_backend.create_channel(
|
||||||
|
authorized=authorized,
|
||||||
|
insecure_ingest=insecure_ingest,
|
||||||
|
latency_mode=latency_mode,
|
||||||
|
name=name,
|
||||||
|
preset=preset,
|
||||||
|
recording_configuration_arn=recording_configuration_arn,
|
||||||
|
tags=tags,
|
||||||
|
channel_type=channel_type,
|
||||||
|
)
|
||||||
|
return json.dumps(dict(channel=channel, streamKey=stream_key))
|
||||||
|
|
||||||
|
def list_channels(self) -> str:
|
||||||
|
filter_by_name = self._get_param("filterByName")
|
||||||
|
filter_by_recording_configuration_arn = self._get_param(
|
||||||
|
"filterByRecordingConfigurationArn"
|
||||||
|
)
|
||||||
|
max_results = self._get_param("maxResults")
|
||||||
|
next_token = self._get_param("nextToken")
|
||||||
|
channels, next_token = self.ivs_backend.list_channels(
|
||||||
|
filter_by_name=filter_by_name,
|
||||||
|
filter_by_recording_configuration_arn=filter_by_recording_configuration_arn,
|
||||||
|
max_results=max_results,
|
||||||
|
next_token=next_token,
|
||||||
|
)
|
||||||
|
return json.dumps(dict(channels=channels, nextToken=next_token))
|
||||||
|
|
||||||
|
def get_channel(self) -> str:
|
||||||
|
arn = self._get_param("arn")
|
||||||
|
channel = self.ivs_backend.get_channel(
|
||||||
|
arn=arn,
|
||||||
|
)
|
||||||
|
return json.dumps(dict(channel=channel))
|
||||||
|
|
||||||
|
def batch_get_channel(self) -> str:
|
||||||
|
arns = self._get_param("arns")
|
||||||
|
channels, errors = self.ivs_backend.batch_get_channel(
|
||||||
|
arns=arns,
|
||||||
|
)
|
||||||
|
return json.dumps(dict(channels=channels, errors=errors))
|
||||||
|
|
||||||
|
def update_channel(self) -> str:
|
||||||
|
arn = self._get_param("arn")
|
||||||
|
authorized = self._get_param("authorized")
|
||||||
|
insecure_ingest = self._get_param("insecureIngest")
|
||||||
|
latency_mode = self._get_param("latencyMode")
|
||||||
|
name = self._get_param("name")
|
||||||
|
preset = self._get_param("preset")
|
||||||
|
recording_configuration_arn = self._get_param("recordingConfigurationArn")
|
||||||
|
channel_type = self._get_param("type")
|
||||||
|
channel = self.ivs_backend.update_channel(
|
||||||
|
arn=arn,
|
||||||
|
authorized=authorized,
|
||||||
|
insecure_ingest=insecure_ingest,
|
||||||
|
latency_mode=latency_mode,
|
||||||
|
name=name,
|
||||||
|
preset=preset,
|
||||||
|
recording_configuration_arn=recording_configuration_arn,
|
||||||
|
channel_type=channel_type,
|
||||||
|
)
|
||||||
|
return json.dumps(dict(channel=channel))
|
||||||
|
|
||||||
|
def delete_channel(self) -> None:
|
||||||
|
arn = self._get_param("arn")
|
||||||
|
self.ivs_backend.delete_channel(
|
||||||
|
arn=arn,
|
||||||
|
)
|
20
moto/ivs/urls.py
Normal file
20
moto/ivs/urls.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
"""ivs base URL and path."""
|
||||||
|
from .responses import IVSResponse
|
||||||
|
|
||||||
|
|
||||||
|
url_bases = [
|
||||||
|
r"https?://ivs\.(.+)\.amazonaws\.com",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
response = IVSResponse()
|
||||||
|
|
||||||
|
|
||||||
|
url_paths = {
|
||||||
|
"{0}/CreateChannel": response.dispatch,
|
||||||
|
"{0}/ListChannels": response.dispatch,
|
||||||
|
"{0}/GetChannel": response.dispatch,
|
||||||
|
"{0}/BatchGetChannel": response.dispatch,
|
||||||
|
"{0}/UpdateChannel": response.dispatch,
|
||||||
|
"{0}/DeleteChannel": response.dispatch,
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
from random import Random
|
from random import Random
|
||||||
import string
|
import string
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
from base64 import urlsafe_b64encode
|
||||||
|
|
||||||
|
|
||||||
HEX_CHARS = list(range(10)) + ["a", "b", "c", "d", "e", "f"]
|
HEX_CHARS = list(range(10)) + ["a", "b", "c", "d", "e", "f"]
|
||||||
@ -30,3 +31,8 @@ class MotoRandom(Random):
|
|||||||
pool += string.digits
|
pool += string.digits
|
||||||
random_str = "".join([self.choice(pool) for i in range(length)])
|
random_str = "".join([self.choice(pool) for i in range(length)])
|
||||||
return random_str.lower() if lower_case else random_str
|
return random_str.lower() if lower_case else random_str
|
||||||
|
|
||||||
|
# Based on https://github.com/python/cpython/blob/main/Lib/secrets.py
|
||||||
|
def token_urlsafe(self, nbytes: int) -> str:
|
||||||
|
randbytes = self.getrandbits(nbytes * 8).to_bytes(nbytes, "little")
|
||||||
|
return urlsafe_b64encode(randbytes).rstrip(b"=").decode("ascii")
|
||||||
|
0
tests/test_ivs/__init__.py
Normal file
0
tests/test_ivs/__init__.py
Normal file
192
tests/test_ivs/test_ivs.py
Normal file
192
tests/test_ivs/test_ivs.py
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
"""Unit tests for ivs-supported APIs."""
|
||||||
|
import boto3
|
||||||
|
from botocore.exceptions import ClientError
|
||||||
|
from pytest import raises
|
||||||
|
from moto import mock_ivs
|
||||||
|
from re import fullmatch
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ivs
|
||||||
|
def test_create_channel_with_name():
|
||||||
|
client = boto3.client("ivs", region_name="eu-west-1")
|
||||||
|
create_response = client.create_channel(name="foo")
|
||||||
|
assert create_response["channel"]["name"] == "foo"
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ivs
|
||||||
|
def test_create_channel_defaults():
|
||||||
|
client = boto3.client("ivs", region_name="eu-west-1")
|
||||||
|
create_response = client.create_channel(name="foo")
|
||||||
|
assert create_response["channel"]["authorized"] is False
|
||||||
|
assert create_response["channel"]["insecureIngest"] is False
|
||||||
|
assert create_response["channel"]["latencyMode"] == "LOW"
|
||||||
|
assert create_response["channel"]["preset"] == ""
|
||||||
|
assert create_response["channel"]["recordingConfigurationArn"] == ""
|
||||||
|
assert create_response["channel"]["tags"] == {}
|
||||||
|
assert create_response["channel"]["type"] == "STANDARD"
|
||||||
|
assert create_response["streamKey"]["tags"] == {}
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ivs
|
||||||
|
def test_create_channel_generated_values():
|
||||||
|
client = boto3.client("ivs", region_name="eu-west-1")
|
||||||
|
create_response = client.create_channel(name="foo")
|
||||||
|
assert fullmatch(r"arn:aws:ivs:.*:channel/.*", create_response["channel"]["arn"])
|
||||||
|
assert create_response["channel"]["ingestEndpoint"]
|
||||||
|
assert create_response["channel"]["playbackUrl"]
|
||||||
|
assert fullmatch(
|
||||||
|
r"arn:aws:ivs:.*:stream-key/.*", create_response["streamKey"]["arn"]
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
create_response["streamKey"]["channelArn"] == create_response["channel"]["arn"]
|
||||||
|
)
|
||||||
|
assert fullmatch(r"sk_.*", create_response["streamKey"]["value"])
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ivs
|
||||||
|
def test_create_channel_with_name_and_recording_configuration():
|
||||||
|
client = boto3.client("ivs", region_name="eu-west-1")
|
||||||
|
create_response = client.create_channel(
|
||||||
|
name="foo",
|
||||||
|
recordingConfigurationArn="blah",
|
||||||
|
)
|
||||||
|
assert create_response["channel"]["name"] == "foo"
|
||||||
|
assert create_response["channel"]["recordingConfigurationArn"] == "blah"
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ivs
|
||||||
|
def test_list_channels_empty():
|
||||||
|
client = boto3.client("ivs", region_name="eu-west-1")
|
||||||
|
list_response = client.list_channels()
|
||||||
|
assert list_response["channels"] == []
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ivs
|
||||||
|
def test_list_channels_one():
|
||||||
|
client = boto3.client("ivs", region_name="eu-west-1")
|
||||||
|
client.create_channel(name="foo")
|
||||||
|
list_response = client.list_channels()
|
||||||
|
assert len(list_response["channels"]) == 1
|
||||||
|
assert list_response["channels"][0]["name"] == "foo"
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ivs
|
||||||
|
def test_list_channels_two():
|
||||||
|
client = boto3.client("ivs", region_name="eu-west-1")
|
||||||
|
client.create_channel(name="foo")
|
||||||
|
client.create_channel(
|
||||||
|
name="bar",
|
||||||
|
recordingConfigurationArn="blah",
|
||||||
|
)
|
||||||
|
list_response = client.list_channels()
|
||||||
|
assert len(list_response["channels"]) == 2
|
||||||
|
assert list_response["channels"][0]["name"] == "foo"
|
||||||
|
assert list_response["channels"][1]["name"] == "bar"
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ivs
|
||||||
|
def test_list_channels_by_name():
|
||||||
|
client = boto3.client("ivs", region_name="eu-west-1")
|
||||||
|
client.create_channel(name="foo")
|
||||||
|
client.create_channel(
|
||||||
|
name="bar",
|
||||||
|
recordingConfigurationArn="blah",
|
||||||
|
)
|
||||||
|
list_response = client.list_channels(filterByName="foo")
|
||||||
|
assert len(list_response["channels"]) == 1
|
||||||
|
assert list_response["channels"][0]["name"] == "foo"
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ivs
|
||||||
|
def test_list_channels_by_recording_configuration():
|
||||||
|
client = boto3.client("ivs", region_name="eu-west-1")
|
||||||
|
client.create_channel(name="foo")
|
||||||
|
client.create_channel(
|
||||||
|
name="bar",
|
||||||
|
recordingConfigurationArn="blah",
|
||||||
|
)
|
||||||
|
list_response = client.list_channels(filterByRecordingConfigurationArn="blah")
|
||||||
|
assert len(list_response["channels"]) == 1
|
||||||
|
assert list_response["channels"][0]["name"] == "bar"
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ivs
|
||||||
|
def test_list_channels_pagination():
|
||||||
|
client = boto3.client("ivs", region_name="eu-west-1")
|
||||||
|
client.create_channel(name="foo")
|
||||||
|
client.create_channel(name="bar")
|
||||||
|
first_list_response = client.list_channels(maxResults=1)
|
||||||
|
assert len(first_list_response["channels"]) == 1
|
||||||
|
assert "nextToken" in first_list_response
|
||||||
|
second_list_response = client.list_channels(
|
||||||
|
maxResults=1, nextToken=first_list_response["nextToken"]
|
||||||
|
)
|
||||||
|
assert len(second_list_response["channels"]) == 1
|
||||||
|
assert "nextToken" not in second_list_response
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ivs
|
||||||
|
def test_get_channel_exists():
|
||||||
|
client = boto3.client("ivs", region_name="eu-west-1")
|
||||||
|
create_response = client.create_channel(name="foo")
|
||||||
|
get_response = client.get_channel(arn=create_response["channel"]["arn"])
|
||||||
|
assert get_response["channel"]["name"] == "foo"
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ivs
|
||||||
|
def test_get_channel_not_exists():
|
||||||
|
client = boto3.client("ivs", region_name="eu-west-1")
|
||||||
|
with raises(ClientError) as exc:
|
||||||
|
client.get_channel(arn="nope")
|
||||||
|
assert exc.value.response["Error"]["Code"] == "ResourceNotFoundException"
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ivs
|
||||||
|
def test_batch_get_channel():
|
||||||
|
client = boto3.client("ivs", region_name="eu-west-1")
|
||||||
|
create_response = client.create_channel(name="foo")
|
||||||
|
batch_get_response = client.batch_get_channel(
|
||||||
|
arns=[create_response["channel"]["arn"]]
|
||||||
|
)
|
||||||
|
assert len(batch_get_response["channels"]) == 1
|
||||||
|
assert batch_get_response["channels"][0]["name"] == "foo"
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ivs
|
||||||
|
def test_update_channel_exists():
|
||||||
|
client = boto3.client("ivs", region_name="eu-west-1")
|
||||||
|
create_response = client.create_channel(
|
||||||
|
name="foo",
|
||||||
|
recordingConfigurationArn="blah",
|
||||||
|
)
|
||||||
|
update_response = client.update_channel(
|
||||||
|
arn=create_response["channel"]["arn"],
|
||||||
|
name="bar",
|
||||||
|
)
|
||||||
|
assert update_response["channel"]["name"] == "bar"
|
||||||
|
assert update_response["channel"]["recordingConfigurationArn"] == "blah"
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ivs
|
||||||
|
def test_update_channel_not_exists():
|
||||||
|
client = boto3.client("ivs", region_name="eu-west-1")
|
||||||
|
with raises(ClientError) as exc:
|
||||||
|
client.update_channel(arn="nope", name="bar")
|
||||||
|
assert exc.value.response["Error"]["Code"] == "ResourceNotFoundException"
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ivs
|
||||||
|
def test_delete_channel_exists():
|
||||||
|
client = boto3.client("ivs", region_name="eu-west-1")
|
||||||
|
create_response = client.create_channel(name="foo")
|
||||||
|
client.delete_channel(arn=create_response["channel"]["arn"])
|
||||||
|
list_response = client.list_channels()
|
||||||
|
assert list_response["channels"] == []
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ivs
|
||||||
|
def test_delete_channel_not_exists():
|
||||||
|
client = boto3.client("ivs", region_name="eu-west-1")
|
||||||
|
with raises(ClientError) as exc:
|
||||||
|
client.delete_channel(arn="nope")
|
||||||
|
assert exc.value.response["Error"]["Code"] == "ResourceNotFoundException"
|
@ -31,7 +31,13 @@ def test_semi_random_hex_strings():
|
|||||||
# Ensure they are different
|
# Ensure they are different
|
||||||
assert fixed_hex != random_hex
|
assert fixed_hex != random_hex
|
||||||
|
|
||||||
# Retrieving another 'fixed' UUID should not return a known UUID
|
# Retrieving another 'fixed' HEX should not return a known HEX
|
||||||
second_hex = mock_random.uuid4()
|
second_hex = mock_random.get_random_hex()
|
||||||
assert second_hex != random_hex
|
assert second_hex != random_hex
|
||||||
assert second_hex != fixed_hex
|
assert second_hex != fixed_hex
|
||||||
|
|
||||||
|
|
||||||
|
def test_semi_random_token_urlsafe():
|
||||||
|
mock_random.seed(42)
|
||||||
|
token = mock_random.token_urlsafe(32)
|
||||||
|
assert token == "nXmxo38xgBzRGmcG-0DWvVdSaEaQO7E-3lYkOenBuCM"
|
||||||
|
Loading…
Reference in New Issue
Block a user