mirror of
https://github.com/yt-dlp/yt-dlp.git
synced 2025-07-27 07:38:30 +00:00
doc: teste(twitter): adicionar testes unitários para a lógica de disponibilidade do Twitter Space
This commit is contained in:
parent
2b6fbda4d4
commit
7b34f22826
1
test/test_AeonCo_2_emyi4z-O0ls.info.json
Normal file
1
test/test_AeonCo_2_emyi4z-O0ls.info.json
Normal file
File diff suppressed because one or more lines are too long
22
test/test_donate.py
Normal file
22
test/test_donate.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import re
|
||||||
|
import requests
|
||||||
|
|
||||||
|
def read_readme():
|
||||||
|
with open("README.md", "r", encoding="utf-8") as f:
|
||||||
|
return f.read()
|
||||||
|
|
||||||
|
def extract_donate_url(content):
|
||||||
|
match = re.search(r'\[!\[Donate\]\(.*?\)\]\((.*?)\)', content)
|
||||||
|
assert match is not None, "Link de doação não encontrado no README"
|
||||||
|
return match.group(1)
|
||||||
|
|
||||||
|
def test_donate_link_is_absolute():
|
||||||
|
content = read_readme()
|
||||||
|
url = extract_donate_url(content)
|
||||||
|
assert url.startswith("https://"), f"URL não é absoluta: {url}"
|
||||||
|
|
||||||
|
def test_donate_link_is_accessible():
|
||||||
|
content = read_readme()
|
||||||
|
url = extract_donate_url(content)
|
||||||
|
response = requests.get(url)
|
||||||
|
assert response.status_code == 200, f"Link inacessível: status {response.status_code} - {url}"
|
54
test/test_gerador_senha.py
Normal file
54
test/test_gerador_senha.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
# tests/test_gerador_senha.py
|
||||||
|
import pytest
|
||||||
|
import re
|
||||||
|
from gerador_senha import gerar_senha
|
||||||
|
|
||||||
|
# Ciclo 1 do projeto original: Garante que o erro é lançado se nenhuma opção for selecionada.
|
||||||
|
def test_ciclo1_excecao_sem_criterios():
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
gerar_senha(5, minusculas=False, maiusculas=False, digitos=False, simbolos=False)
|
||||||
|
|
||||||
|
# Ciclo 2: Nosso alvo para o RED/GREEN de agora.
|
||||||
|
def test_ciclo2_minusculas_apenas():
|
||||||
|
senha = gerar_senha(5, minusculas=True, maiusculas=False, digitos=False, simbolos=False)
|
||||||
|
assert len(senha) == 5
|
||||||
|
assert re.fullmatch(r'[a-z]{5}', senha)
|
||||||
|
|
||||||
|
# Ciclo 3: Teste para minúsculas e maiúsculas.
|
||||||
|
def test_ciclo3_uma_maiuscula():
|
||||||
|
senha = gerar_senha(10, minusculas=True, maiusculas=True, digitos=False, simbolos=False)
|
||||||
|
assert len(senha) == 10
|
||||||
|
assert any(c.isupper() for c in senha)
|
||||||
|
assert any(c.islower() for c in senha)
|
||||||
|
assert all(c.isalpha() for c in senha)
|
||||||
|
|
||||||
|
# Ciclo 4: Teste para minúsculas e dígitos.
|
||||||
|
def test_ciclo4_um_digito():
|
||||||
|
senha = gerar_senha(12, minusculas=True, maiusculas=False, digitos=True, simbolos=False)
|
||||||
|
assert len(senha) == 12
|
||||||
|
assert any(c.isdigit() for c in senha)
|
||||||
|
assert all(c.islower() or c.isdigit() for c in senha)
|
||||||
|
|
||||||
|
# Ciclo 5: Teste para apenas símbolos.
|
||||||
|
def test_ciclo5_um_simbolo():
|
||||||
|
senha = gerar_senha(6, minusculas=False, maiusculas=False, digitos=False, simbolos=True)
|
||||||
|
assert len(senha) == 6
|
||||||
|
assert any(c in "!@#$%&*" for c in senha)
|
||||||
|
assert all(c in "!@#$%&*" for c in senha)
|
||||||
|
|
||||||
|
# Ciclo 6: Teste para todas as categorias.
|
||||||
|
def test_ciclo6_uma_de_cada_categoria():
|
||||||
|
senha = gerar_senha(16, minusculas=True, maiusculas=True, digitos=True, simbolos=True)
|
||||||
|
assert len(senha) == 16
|
||||||
|
assert any(c.islower() for c in senha)
|
||||||
|
assert any(c.isupper() for c in senha)
|
||||||
|
assert any(c.isdigit() for c in senha)
|
||||||
|
assert any(c in "!@#$%&*" for c in senha)
|
||||||
|
|
||||||
|
# Ciclo 7: Teste para minúsculas e símbolos.
|
||||||
|
def test_ciclo7_minusculas_simbolos():
|
||||||
|
senha = gerar_senha(10, minusculas=True, maiusculas=False, digitos=False, simbolos=True)
|
||||||
|
assert len(senha) == 10
|
||||||
|
assert any(c.islower() for c in senha)
|
||||||
|
assert any(c in "!@#$%&*" for c in senha)
|
||||||
|
assert all(c.islower() or c in "!@#$%&*" for c in senha)
|
39
test/test_twitter_spaces_availability.py
Normal file
39
test/test_twitter_spaces_availability.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
def is_twitter_space_available(metadata):
|
||||||
|
"""
|
||||||
|
Verifica se um Twitter Space está disponível para download.
|
||||||
|
Um espaço só é considerado disponível se:
|
||||||
|
- Não está ao vivo
|
||||||
|
- Não está agendado (upcoming)
|
||||||
|
- Não é privado
|
||||||
|
"""
|
||||||
|
return (
|
||||||
|
metadata.get("is_live") is False
|
||||||
|
and metadata.get("live_status") not in ["is_upcoming", "upcoming"]
|
||||||
|
and metadata.get("availability") != "private"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_space_is_available_when_not_live_not_upcoming_and_not_private():
|
||||||
|
metadata = {
|
||||||
|
'is_live': False,
|
||||||
|
'live_status': None,
|
||||||
|
'availability': 'public'
|
||||||
|
}
|
||||||
|
assert is_twitter_space_available(metadata) is True, "Twitter Space deveria ser considerado disponível"
|
||||||
|
|
||||||
|
def test_space_is_not_available_when_upcoming():
|
||||||
|
metadata = {
|
||||||
|
'is_live': False,
|
||||||
|
'live_status': "is_upcoming",
|
||||||
|
'availability': 'public'
|
||||||
|
}
|
||||||
|
assert is_twitter_space_available(metadata) is False, "Twitter Space agendado não deveria ser considerado disponível"
|
||||||
|
|
||||||
|
def test_space_is_not_available_when_private():
|
||||||
|
metadata = {
|
||||||
|
'is_live': False,
|
||||||
|
'live_status': None,
|
||||||
|
'availability': 'private'
|
||||||
|
}
|
||||||
|
assert is_twitter_space_available(metadata) is False, "Twitter Space privado não deveria ser considerado disponível"
|
153879
unicodedata
Normal file
153879
unicodedata
Normal file
File diff suppressed because it is too large
Load Diff
42
yt_dlp/postprocessor/writedescription.py
Normal file
42
yt_dlp/postprocessor/writedescription.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# yt_dlp/postprocessor/writedescription.py
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from .common import PostProcessor
|
||||||
|
|
||||||
|
class WriteDescriptionPP(PostProcessor):
|
||||||
|
def __init__(self, downloader, **kwargs):
|
||||||
|
# O nome do PP que será usado na linha de comando é 'writedescription'
|
||||||
|
super().__init__(downloader)
|
||||||
|
|
||||||
|
# O método run é o que executa a lógica do nosso PP
|
||||||
|
def run(self, info):
|
||||||
|
"""Escreve a descrição em um arquivo .description"""
|
||||||
|
|
||||||
|
# Pega o nome do arquivo de vídeo que já foi processado
|
||||||
|
filepath = info.get('filepath')
|
||||||
|
if not filepath:
|
||||||
|
self.to_screen('ERROR: Não foi possível encontrar o caminho do arquivo de vídeo.')
|
||||||
|
return [], info
|
||||||
|
|
||||||
|
# Pega a descrição do dicionário de informações
|
||||||
|
description = info.get('description')
|
||||||
|
if not description:
|
||||||
|
self.to_screen('Vídeo não tem descrição, pulando a criação do arquivo.')
|
||||||
|
return [], info
|
||||||
|
|
||||||
|
# Monta o nome do novo arquivo de descrição
|
||||||
|
desc_filename = os.path.splitext(filepath)[0] + '.description'
|
||||||
|
|
||||||
|
self.to_screen(f'Writing description to: {desc_filename}')
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Escreve o arquivo
|
||||||
|
with open(desc_filename, 'w', encoding='utf-8') as f:
|
||||||
|
f.write(description)
|
||||||
|
except (IOError, OSError) as err:
|
||||||
|
self.report_error(f'Cannot write description file: {err}')
|
||||||
|
return [], info
|
||||||
|
|
||||||
|
# Retorna a lista de arquivos e o dicionário, como esperado pelo yt-dlp
|
||||||
|
return [], info
|
Loading…
Reference in New Issue
Block a user