dummy_agones/
lib.rs

1mod agones_game_server_state;
2mod agones_server;
3mod yaml_server;
4
5pub mod sdk {
6    tonic::include_proto!("agones.dev.sdk");
7}
8
9use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
10
11pub use agones_server::AgonesServer;
12pub use agones_server::DEFAULT_AGONES_PORT;
13use hyper::server::conn::AddrIncoming;
14
15impl yaml_server::Store<sdk::GameServer> for AgonesServer {
16    fn get(&self) -> sdk::GameServer {
17        self.game_server()
18    }
19
20    fn set(&self, value: sdk::GameServer) {
21        self.set_game_server(value);
22    }
23}
24
25pub struct Server {
26    make_service: hybrid_service::HybridMakeService<
27        axum::routing::IntoMakeService<axum::Router>,
28        tonic::transport::server::Routes,
29    >,
30}
31
32impl Server {
33    /// Start the server with the default configuration (localhost with default Agones port).
34    pub async fn serve(&self) -> Result<(), hyper::Error> {
35        let incoming = AddrIncoming::bind(&SocketAddr::V4(SocketAddrV4::new(
36            Ipv4Addr::LOCALHOST,
37            DEFAULT_AGONES_PORT,
38        )))?;
39        self.serve_with_incoming(incoming).await
40    }
41
42    /// Spawn the server in background with random port.
43    pub async fn spawn_on_random_port(&self) -> Result<u16, hyper::Error> {
44        let incoming =
45            AddrIncoming::bind(&SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0)))?;
46        let port = incoming.local_addr().port();
47        let make_service = self.make_service.clone();
48        tokio::spawn(hyper::Server::builder(incoming).serve(make_service));
49        Ok(port)
50    }
51
52    /// Start the server with the given incoming.
53    pub async fn serve_with_incoming(&self, incoming: AddrIncoming) -> Result<(), hyper::Error> {
54        hyper::Server::builder(incoming)
55            .serve(self.make_service.clone())
56            .await
57    }
58}
59
60impl Default for Server {
61    fn default() -> Self {
62        let agones_server = AgonesServer::default();
63        let make_service = hybrid_service::hybrid(
64            yaml_server::create_service(agones_server.clone()),
65            agones_server.into_service(),
66        );
67        Self { make_service }
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use std::sync::Arc;
74
75    use parking_lot::RwLock;
76    use tokio_stream::StreamExt;
77
78    use crate::agones_game_server_state::GameServerState;
79
80    use super::*;
81
82    struct TestServer {
83        port: u16,
84        sdk: agones::Sdk,
85    }
86
87    impl TestServer {
88        async fn new() -> Self {
89            let port = Server::default().spawn_on_random_port().await.unwrap();
90            let sdk = agones::Sdk::new(port.into(), None).await.unwrap();
91            Self { port, sdk }
92        }
93
94        async fn http_get(&self) -> reqwest::Response {
95            let client = reqwest::Client::new();
96            client
97                .get(format!("http://localhost:{}", self.port))
98                .send()
99                .await
100                .unwrap()
101        }
102
103        async fn get(&self) -> sdk::GameServer {
104            let response = self.http_get().await;
105            serde_yaml::from_str(&response.text().await.unwrap()).unwrap()
106        }
107
108        async fn post(&self, gs: &sdk::GameServer) -> sdk::GameServer {
109            let response = reqwest::Client::new()
110                .post(format!("http://localhost:{}", self.port))
111                .header(reqwest::header::CONTENT_TYPE, "application/yaml")
112                .body(serde_yaml::to_vec(gs).unwrap())
113                .send()
114                .await
115                .unwrap()
116                .error_for_status()
117                .unwrap();
118            serde_yaml::from_str(&response.text().await.unwrap()).unwrap()
119        }
120    }
121
122    #[tokio::test]
123    async fn should_have_default_game_server_after_start_in_http_interface() {
124        // Arrange
125        let test_server = TestServer::new().await;
126
127        // Act
128
129        // Assert
130        let gs = test_server.get().await;
131        assert_eq!(gs, Default::default());
132    }
133
134    #[tokio::test]
135    async fn should_have_default_game_server_after_start_in_agones_sdk() {
136        // Arrange
137        let mut test_server = TestServer::new().await;
138
139        // Act
140
141        // Assert
142        let gs = test_server.sdk.get_gameserver().await.unwrap();
143        assert_eq!(gs, Default::default());
144    }
145
146    #[tokio::test]
147    async fn sdk_ready_should_be_visible_in_http_interface() {
148        // Arrange
149        let mut test_server = TestServer::new().await;
150
151        // Act
152        test_server.sdk.ready().await.unwrap();
153
154        // Assert
155        let gs = test_server.get().await;
156        assert_eq!(gs.status.unwrap().state, GameServerState::Ready.to_string());
157    }
158
159    #[tokio::test]
160    async fn updates_via_http_should_be_visible_in_agones_sdk() {
161        // Arrange
162        let mut test_server = TestServer::new().await;
163
164        // Act
165        test_server
166            .post(&sdk::GameServer {
167                status: Some(sdk::game_server::Status {
168                    state: GameServerState::Ready.to_string(),
169                    ..Default::default()
170                }),
171                ..Default::default()
172            })
173            .await;
174
175        // Assert
176        let gs = test_server.sdk.get_gameserver().await.unwrap();
177        assert_eq!(gs.status.unwrap().state, GameServerState::Ready.to_string());
178    }
179
180    #[tokio::test]
181    async fn updates_via_http_should_be_visible_in_agones_sdk_stream() {
182        // Arrange
183        let mut test_server = TestServer::new().await;
184        let last_game_server = Arc::new(RwLock::new(None));
185
186        // Act
187        let mut stream = test_server.sdk.watch_gameserver().await.unwrap();
188        tokio::spawn({
189            let last_game_server = last_game_server.clone();
190            async move {
191                while let Some(gs) = stream.next().await {
192                    if let Ok(gs) = gs {
193                        *last_game_server.write() = Some(gs);
194                    }
195                }
196            }
197        });
198        test_server
199            .post(&sdk::GameServer {
200                status: Some(sdk::game_server::Status {
201                    state: GameServerState::Ready.to_string(),
202                    ..Default::default()
203                }),
204                ..Default::default()
205            })
206            .await;
207        tokio::time::sleep(std::time::Duration::from_millis(100)).await;
208
209        // Assert
210        let gs = last_game_server.read().clone().unwrap();
211        assert_eq!(gs.status.unwrap().state, GameServerState::Ready.to_string());
212    }
213
214    #[tokio::test]
215    async fn updates_via_http_should_return_updated_game_server() {
216        // Arrange
217        let test_server = TestServer::new().await;
218        let game_server = sdk::GameServer {
219            status: Some(sdk::game_server::Status {
220                state: GameServerState::Ready.to_string(),
221                ..Default::default()
222            }),
223            ..Default::default()
224        };
225
226        // Act
227        let returned_game_server = test_server.post(&game_server).await;
228
229        // Assert
230        assert_eq!(game_server, returned_game_server);
231    }
232}