Skip to content

Commit

Permalink
Merge pull request #306 from madeofpendletonwool/testing
Browse files Browse the repository at this point in the history
Merging basic tests
  • Loading branch information
madeofpendletonwool authored Nov 7, 2024
2 parents 5377b06 + 453d980 commit fd79ac5
Show file tree
Hide file tree
Showing 47 changed files with 3,112 additions and 872 deletions.
103 changes: 103 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
name: Pinepods CI

on:
pull_request:
branches: [main]
push:
branches: [main]
workflow_dispatch:

jobs:
backend-tests:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:latest
env:
POSTGRES_USER: test_user
POSTGRES_PASSWORD: test_password
POSTGRES_DB: test_db
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v3

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.x"

- name: Setup test environment
run: |
chmod +x ./setup-tests.sh
./setup-tests.sh
- name: Run backend tests
env:
TEST_MODE: true
DB_HOST: localhost
DB_PORT: 5432
DB_USER: test_user
DB_PASSWORD: test_password
DB_NAME: test_db
DB_TYPE: postgresql
TEST_DB_TYPE: postgresql
PYTHONPATH: ${{ github.workspace }}
run: |
chmod +x ./run-tests.sh
./run-tests.sh postgresql
frontend-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- uses: hecrj/setup-rust-action@v2
with:
rust-version: 1.81
targets: wasm32-unknown-unknown

# Install cargo-binstall for other OSes using the standard method
- name: Install cargo-binstall
if: matrix.os != 'macos-latest'
uses: cargo-bins/cargo-binstall@main

- name: Depends install
if: ${{ env.DEPENDS_SETUP == 'true' }}
run: |
sudo apt update
sudo apt install -qy libgtk-3-dev
sudo apt-get install -y libwebkit2gtk-4.0-dev libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
- name: wasm-addition
run: |
rustup target add wasm32-unknown-unknown
- name: Install Trunk
run: |
cargo binstall trunk -y
- name: Run frontend tests
working-directory: ./web
run: |
RUSTFLAGS="--cfg=web_sys_unstable_apis" cargo test --features server_build -- --nocapture
# docker-build:
# runs-on: ubuntu-latest
# needs: [backend-tests, frontend-tests]
# steps:
# - uses: actions/checkout@v3

# - name: Set up Docker Buildx
# uses: docker/setup-buildx-action@v2

# - name: Build and test Docker image
# run: |
# docker build -t pinepods:test .
# docker run --rm pinepods:test /bin/sh -c "python3 -m pytest /pinepods/tests/"
37 changes: 36 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,39 @@ clients/mac-app/pinepods.spec
web/target/*
web/.idea/*
web/Cargo.lock
keystore.properties
keystore.properties


# Virtual Environment
venv/
.venv/
ENV/

# Python cache files
__pycache__/
*.py[cod]
*$py.class
.pytest_cache/
.coverage
coverage.xml
.hypothesis/

# Environment variables
.env
.env.test

# IDE specific files
.vscode/
.idea/
*.swp
*.swo

# Test database
*.sqlite3
*.db

# Log files
*.log

# Local test directory
tests_local/
123 changes: 86 additions & 37 deletions Backend/pinepods_backend/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,17 @@ use actix_cors::Cors;
struct SearchQuery {
query: Option<String>,
index: Option<String>,
search_type: Option<String>, // Added for specifying search type
search_type: Option<String>,
}

#[derive(Deserialize)]
struct PodcastQuery {
id: String,
}

async fn search_handler(query: web::Query<SearchQuery>) -> impl Responder {
println!("search_handler called");

// Check if the query parameters are empty and return 200 OK immediately if they are
if query.query.is_none() && query.index.is_none() {
println!("Empty query and index - returning 200 OK");
return HttpResponse::Ok().body("Test connection successful");
Expand All @@ -40,20 +44,10 @@ async fn search_handler(query: web::Query<SearchQuery>) -> impl Responder {

client.get(&itunes_search_url).send().await
} else {
// Determine the correct Podcast Index API endpoint based on search_type
let api_key = match env::var("API_KEY") {
Ok(key) => key,
Err(_) => {
println!("API_KEY not set in the environment");
return HttpResponse::InternalServerError().body("API_KEY not set");
}
};
let api_secret = match env::var("API_SECRET") {
Ok(secret) => secret,
Err(_) => {
println!("API_SECRET not set in the environment");
return HttpResponse::InternalServerError().body("API_SECRET not set");
}
// Podcast Index API search
let (api_key, api_secret) = match get_api_credentials() {
Ok(creds) => creds,
Err(response) => return response,
};

let encoded_search_term = urlencoding::encode(&search_term);
Expand All @@ -72,33 +66,87 @@ async fn search_handler(query: web::Query<SearchQuery>) -> impl Responder {

println!("Using Podcast Index search URL: {}", podcast_search_url);

let epoch_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs().to_string();
let data_to_hash = format!("{}{}{}", api_key, api_secret, epoch_time);

let mut hasher = Sha1::new();
hasher.update(data_to_hash.as_bytes());
let sha_1 = format!("{:x}", hasher.finalize());

let mut headers = HeaderMap::new();
headers.insert("X-Auth-Date", HeaderValue::from_str(&epoch_time).unwrap_or_else(|e| {
error!("Failed to insert X-Auth-Date header: {:?}", e);
std::process::exit(1);
}));
headers.insert("X-Auth-Key", HeaderValue::from_str(&api_key).unwrap_or_else(|e| {
error!("Failed to insert X-Auth-Key header: {:?}", e);
std::process::exit(1);
}));
headers.insert("Authorization", HeaderValue::from_str(&sha_1).unwrap_or_else(|e| {
error!("Failed to insert Authorization header: {:?}", e);
std::process::exit(1);
}));
headers.insert(USER_AGENT, HeaderValue::from_static("MyPodcastApp/1.0")); // Use your custom User-Agent here
let headers = match create_auth_headers(&api_key, &api_secret) {
Ok(h) => h,
Err(response) => return response,
};

println!("Final Podcast Index URL: {}", podcast_search_url);

client.get(&podcast_search_url).headers(headers).send().await
};

handle_response(response).await
}

async fn podcast_handler(query: web::Query<PodcastQuery>) -> impl Responder {
println!("podcast_handler called");

let podcast_id = &query.id;
let client = reqwest::Client::new();

let (api_key, api_secret) = match get_api_credentials() {
Ok(creds) => creds,
Err(response) => return response,
};

let podcast_url = format!("https://api.podcastindex.org/api/1.0/podcasts/byfeedid?id={}", podcast_id);
println!("Using Podcast Index URL: {}", podcast_url);

let headers = match create_auth_headers(&api_key, &api_secret) {
Ok(h) => h,
Err(response) => return response,
};

let response = client.get(&podcast_url).headers(headers).send().await;
handle_response(response).await
}

fn get_api_credentials() -> Result<(String, String), HttpResponse> {
let api_key = match env::var("API_KEY") {
Ok(key) => key,
Err(_) => {
println!("API_KEY not set in the environment");
return Err(HttpResponse::InternalServerError().body("API_KEY not set"));
}
};
let api_secret = match env::var("API_SECRET") {
Ok(secret) => secret,
Err(_) => {
println!("API_SECRET not set in the environment");
return Err(HttpResponse::InternalServerError().body("API_SECRET not set"));
}
};
Ok((api_key, api_secret))
}

fn create_auth_headers(api_key: &str, api_secret: &str) -> Result<HeaderMap, HttpResponse> {
let epoch_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs().to_string();
let data_to_hash = format!("{}{}{}", api_key, api_secret, epoch_time);

let mut hasher = Sha1::new();
hasher.update(data_to_hash.as_bytes());
let sha_1 = format!("{:x}", hasher.finalize());

let mut headers = HeaderMap::new();
headers.insert("X-Auth-Date", HeaderValue::from_str(&epoch_time).unwrap_or_else(|e| {
error!("Failed to insert X-Auth-Date header: {:?}", e);
std::process::exit(1);
}));
headers.insert("X-Auth-Key", HeaderValue::from_str(api_key).unwrap_or_else(|e| {
error!("Failed to insert X-Auth-Key header: {:?}", e);
std::process::exit(1);
}));
headers.insert("Authorization", HeaderValue::from_str(&sha_1).unwrap_or_else(|e| {
error!("Failed to insert Authorization header: {:?}", e);
std::process::exit(1);
}));
headers.insert(USER_AGENT, HeaderValue::from_static("PodPeopleDB/1.0"));

Ok(headers)
}

async fn handle_response(response: Result<reqwest::Response, reqwest::Error>) -> HttpResponse {
match response {
Ok(resp) => {
if resp.status().is_success() {
Expand Down Expand Up @@ -137,6 +185,7 @@ async fn main() -> std::io::Result<()> {
App::new()
.wrap(Cors::default().allow_any_origin().allow_any_method().allow_any_header())
.route("/api/search", web::get().to(search_handler))
.route("/api/podcast", web::get().to(podcast_handler))
})
.bind("0.0.0.0:5000")?
.run()
Expand Down
Loading

0 comments on commit fd79ac5

Please sign in to comment.