@lexho111/plainblog 0.0.5 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.eslintignore ADDED
File without changes
package/.eslintrc.json ADDED
File without changes
package/Article.js CHANGED
@@ -9,6 +9,7 @@ export default class Article {
9
9
  }
10
10
 
11
11
  getContentShort() {
12
+ if (this.content.length < 400) return this.getContent();
12
13
  let contentShort = this.content.substring(0, 400);
13
14
  contentShort += "...";
14
15
  return contentShort;
package/Blog.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import http from "http";
2
+ import fetch from "node-fetch";
2
3
  import { promises as fs } from "fs";
3
4
  import { URLSearchParams } from "url";
4
5
  import Article from "./Article.js";
@@ -11,6 +12,33 @@ export default class Blog {
11
12
  this.filename = null;
12
13
  }
13
14
 
15
+ async fetchDataFromApi(apiUrl) {
16
+ try {
17
+ const response = await fetch(apiUrl);
18
+
19
+ if (!response.ok) {
20
+ throw new Error(`HTTP error! status: ${response.status}`);
21
+ }
22
+
23
+ const data = await response.json();
24
+ return data;
25
+ } catch (error) {
26
+ console.error("Failed to fetch data:", error);
27
+ return null;
28
+ }
29
+ }
30
+
31
+ async postDataToApi(apiUrl, postData) {
32
+ const response = await fetch(apiUrl, {
33
+ method: "POST",
34
+ body: JSON.stringify(postData),
35
+ headers: { "Content-Type": "application/json" },
36
+ });
37
+
38
+ const data = await response.json();
39
+ return data;
40
+ }
41
+
14
42
  setTitle(title) {
15
43
  this.title = title;
16
44
  }
@@ -23,8 +51,15 @@ export default class Blog {
23
51
  this.style = style;
24
52
  }
25
53
 
26
- startServer(port = 8080) {
27
- const server = http.createServer((req, res) => {
54
+ APIUrl = "";
55
+
56
+ setAPI(APIUrl) {
57
+ this.APIUrl = APIUrl;
58
+ }
59
+
60
+ async startServer(port = 8080) {
61
+ if (this.APIUrl.length > 0) await this.loadFromAPI(this.APIUrl);
62
+ const server = http.createServer(async (req, res) => {
28
63
  if (req.method === "POST") {
29
64
  let body = "";
30
65
  req.on("data", (chunk) => {
@@ -36,10 +71,16 @@ export default class Blog {
36
71
  const content = params.get("content");
37
72
 
38
73
  if (title && content) {
39
- this.addArticle(new Article(title, content));
40
- // Auto-save if a file has been loaded or saved before
41
- if (this.filename) {
42
- await this.save();
74
+ const newArticle = new Article(title, content);
75
+ this.addArticle(newArticle);
76
+
77
+ // Post the new article to the backend API
78
+ try {
79
+ const postUrl = new URL(this.APIUrl);
80
+ postUrl.pathname = "/blog/articles";
81
+ await this.postDataToApi(postUrl.toString(), newArticle);
82
+ } catch (error) {
83
+ console.error("Failed to post new article to API:", error);
43
84
  }
44
85
  }
45
86
 
@@ -48,8 +89,9 @@ export default class Blog {
48
89
  res.end();
49
90
  });
50
91
  } else {
92
+ const html = await this.toHTML();
51
93
  res.writeHead(200, { "Content-Type": "text/html; charset=UTF-8" });
52
- res.end(this.toHTML());
94
+ res.end(html);
53
95
  }
54
96
  });
55
97
 
@@ -59,6 +101,7 @@ export default class Blog {
59
101
  }
60
102
 
61
103
  async save(filename = this.filename) {
104
+ if (this.APIUrl.length > 0) await this.loadFromAPI(this.APIUrl);
62
105
  if (!filename) {
63
106
  console.error("Error: Filename not provided and not set previously.");
64
107
  return;
@@ -80,12 +123,30 @@ export default class Blog {
80
123
  async load(filename) {
81
124
  this.filename = filename;
82
125
  const data = await fs.readFile(filename, "utf8");
83
- const { title, articles } = JSON.parse(data);
84
- this.title = title;
85
- this.articles = articles;
126
+ const jsonData = JSON.parse(data);
127
+ this.title = jsonData.title;
128
+ this.articles = jsonData.articles.map(
129
+ (article) => new Article(article.title, article.content)
130
+ );
86
131
  }
87
132
 
88
- print() {
133
+ async loadFromAPI(apiUrl) {
134
+ const data = await this.fetchDataFromApi(apiUrl);
135
+ if (data) {
136
+ this.articles = []; // Clear existing articles before loading new ones
137
+ this.setTitle(data.title);
138
+ // Assuming the API returns an array of objects with title and content
139
+ if (data.articles && Array.isArray(data.articles)) {
140
+ for (const articleData of data.articles) {
141
+ this.addArticle(new Article(articleData.title, articleData.content));
142
+ }
143
+ this.apiDataLoaded = true; // Mark that we have successfully loaded data
144
+ }
145
+ }
146
+ }
147
+
148
+ async print() {
149
+ if (this.APIUrl.length > 0) await this.loadFromAPI(this.APIUrl);
89
150
  console.log(`# ${this.title}`);
90
151
  for (const article of this.articles) {
91
152
  console.log(`## ${article.title}`);
@@ -93,37 +154,40 @@ export default class Blog {
93
154
  }
94
155
  }
95
156
 
96
- toHTML() {
97
- let html = `
98
- <!DOCTYPE html>
99
- <html lang="de">
100
- <head>
101
- <meta charset="UTF-8">
102
- <title>${this.title}</title>
103
- <style>${this.style}</style>
104
- </head>
105
- <body>`;
106
- html += `<h1>${this.title}</h1><div style="width: 500px;">`;
107
-
108
- html += `
157
+ async toHTML() {
158
+ // If we have an API URL and haven't loaded data yet, load it now.
159
+ if (this.APIUrl && !this.apiDataLoaded) {
160
+ await this.loadFromAPI(this.APIUrl);
161
+ }
162
+
163
+ return `<!DOCTYPE html>
164
+ <html lang="de">
165
+ <head>
166
+ <meta charset="UTF-8">
167
+ <title>${this.title}</title>
168
+ <style>${this.style}</style>
169
+ </head>
170
+ <body>
171
+ <h1>${this.title}</h1>
172
+ <div style="width: 500px;">
109
173
  <form action="/" method="POST">
110
174
  <h3>Add a New Article</h3>
111
175
  <input type="text" name="title" placeholder="Article Title" required style="display: block; width: 300px; margin-bottom: 10px;">
112
176
  <textarea name="content" placeholder="Article Content" required style="display: block; width: 300px; height: 100px; margin-bottom: 10px;"></textarea>
113
177
  <button type="submit">Add Article</button>
114
178
  </form>
115
- <hr>`;
116
-
117
- for (const article of this.articles) {
118
- html += `
179
+ <hr>
180
+ ${this.articles
181
+ .map(
182
+ (article) => `
119
183
  <article>
120
184
  <h2>${article.title}</h2>
121
185
  <p>${article.getContentShort()}</p>
122
- </article>`;
123
- }
124
-
125
- html += `</div></body></html>`;
126
-
127
- return html;
186
+ </article>`
187
+ )
188
+ .join("")}
189
+ </div>
190
+ </body>
191
+ </html>`;
128
192
  }
129
193
  }
package/README.md CHANGED
@@ -1,21 +1,69 @@
1
+ # Plainblog
2
+
3
+ Plainblog is a simple blog generator to help you to set up and to maintain a minimalistic **single-page blog**. You can add new articles directly in the browser. Articles are stored in a file or a database specified via an API.
4
+
5
+ ## Install
6
+
1
7
  ```
2
- import { Blog, Article } from "@lexho111/plainblog";
8
+ npm install @lexho111/plainblog
9
+ ```
10
+
11
+ ## Quick Start
3
12
 
4
- const bl og = new Blog();
13
+ ```
14
+ import { Blog } from "@lexho111/plainblog";
15
+
16
+ const blog = new Blog();
5
17
  blog.setTitle("My Blog");
6
18
  blog.setStyle("body { font-family: Arial, sans-serif; } h1 { color: #333; }");
7
19
 
8
- const article1 = new Article("My First Post", "Hello world!");
9
- blog.addArticle(article1);
20
+ blog.startServer(8080);
21
+ ```
22
+
23
+ Now you can start to add articles to your blog via your webbrowser on `http://localhost:8080`.
24
+
25
+ ## More Features
26
+
27
+ set an API to fetch data from an external database
28
+
29
+ ```
30
+ blog.setAPI("http://localhost:8081/blog")
31
+ ```
32
+
33
+ ### run api server with sqlite database
34
+
35
+ ```
36
+ import { Blog, storageserver} from "@lexho111/plainblog";
37
+ await storageserver("sqlite", 8081); // you can use a postgres db too
38
+ const blog = new Blog();
39
+ blog.setAPI(storageserver.getAPIURL());
40
+ blog.setStyle("body { font-family: Arial, sans-serif; } h1 { color: #333; }");
10
41
 
11
- const article2 = new Article(
12
- "Good Evening!",
13
- "Good Evening, Ladies and Gentleman!"
14
- );
15
- blog.addArticle(article2);
16
42
  blog.startServer(8080);
17
43
  ```
18
44
 
45
+ ### run api server with postgres database
46
+
47
+ ```
48
+ docker run -p 5432:5432 --name postgresdb --restart always -e POSTGRES_USER=user -e POSTGRES_PASSWORD=password -e POSTGRES_DB=blog -v db_data:/var/lib/postgresql/data -d postgres:13
49
+ ```
50
+
51
+ ```
52
+ import { Blog, storageserver} from "@lexho111/plainblog";
53
+
54
+ storageserver.setUsername("username");
55
+ storageserver.setPassword("password");
56
+ storageserver.setHost("localhost");
57
+ await storageserver.start("postgres", 8081);
58
+ const blog = new Blog();
59
+ blog.setAPI(storageserver.getAPIURL());
60
+ blog.setStyle("body { font-family: Arial, sans-serif; } h1 { color: #333; }");
61
+
62
+ blog.startServer(8080);
63
+ ```
64
+
65
+ save data to file
66
+
19
67
  ```
20
68
  # save your blog to 'myblog.json'
21
69
  await blog.save("myblog.json");
@@ -23,3 +71,9 @@ await blog.save("myblog.json");
23
71
  # load data from 'myblog.json'
24
72
  await blog.load("myblog.json");
25
73
  ```
74
+
75
+ print your blog articles in markdown
76
+
77
+ ```
78
+ blog.print()
79
+ ```
package/api-server.js ADDED
@@ -0,0 +1,175 @@
1
+ import http from "http";
2
+ import { Sequelize, DataTypes } from "sequelize";
3
+
4
+ // This is a simple API server that provides blog data.
5
+ // This is the data that our API will serve, similar to a myblog.json file.
6
+ const blogData = {
7
+ title: "My Blog from the API",
8
+ articles: [
9
+ {
10
+ title: "First API Post",
11
+ content: "This article was loaded from our new external API server!",
12
+ },
13
+ {
14
+ title: "REST is Cool",
15
+ content:
16
+ "Using a REST API allows us to separate our frontend from our backend.",
17
+ },
18
+ ],
19
+ };
20
+
21
+ let username;
22
+ let password;
23
+ let host;
24
+ const dbport = 5432;
25
+ const dbname = process.env.POSTGRES_DB || "blog";
26
+
27
+ export function setUsername(username1) {
28
+ username = username1;
29
+ }
30
+ export function setPassword(password1) {
31
+ password = password1;
32
+ }
33
+ export function setHost(host1) {
34
+ host = host1;
35
+ }
36
+
37
+ let serverPort = 8081; // Default port
38
+
39
+ export async function start(databasetype, port = 8081) {
40
+ let sequelize;
41
+ serverPort = port; // Update the port for getAPIURL
42
+
43
+ if (databasetype === "sqlite") {
44
+ sequelize = new Sequelize({
45
+ dialect: "sqlite",
46
+ storage: "./blog.db",
47
+ logging: false,
48
+ });
49
+ } else if (databasetype === "postgres") {
50
+ if (!username || !password || !host) {
51
+ throw new Error(
52
+ "PostgreSQL credentials not set. Please use setUsername(), setPassword(), and setHost() before starting the server."
53
+ );
54
+ }
55
+
56
+ sequelize = new Sequelize(
57
+ `postgres://${username}:${password}@${host}:${dbport}/${dbname}`,
58
+ { logging: false }
59
+ );
60
+ }
61
+
62
+ const Article = sequelize.define(
63
+ "Article",
64
+ {
65
+ title: DataTypes.STRING,
66
+ content: DataTypes.STRING,
67
+ },
68
+ {
69
+ timestamps: false, // Assuming you don't need createdAt/updatedAt for this simple model
70
+ }
71
+ );
72
+
73
+ try {
74
+ // The connectWithRetry logic is now integrated here and properly awaited.
75
+ let retries = 5;
76
+ while (retries) {
77
+ try {
78
+ await sequelize.authenticate();
79
+ console.log("Connection has been established successfully.");
80
+
81
+ // Sync all models and populate data
82
+ await sequelize.sync({ alter: true });
83
+ console.log("All models were synchronized successfully.");
84
+
85
+ if ((await Article.count()) === 0) {
86
+ for (const articleData of blogData.articles) {
87
+ await Article.create(articleData);
88
+ }
89
+ console.log("Initial blog data populated.");
90
+ }
91
+ break; // Success, exit retry loop
92
+ } catch (err) {
93
+ // For sqlite, we don't need to retry, just throw the error.
94
+ if (databasetype === "sqlite") throw err;
95
+
96
+ console.error("Unable to connect to the database:", err.name);
97
+ retries -= 1;
98
+ console.log(`Retries left: ${retries}`);
99
+ if (retries === 0) throw err; // Throw error if max retries reached
100
+ await new Promise((res) => setTimeout(res, 5000));
101
+ }
102
+ }
103
+
104
+ const server = http.createServer(async (req, res) => {
105
+ // Set CORS headers for all responses
106
+ res.setHeader("Access-Control-Allow-Origin", "*");
107
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
108
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
109
+
110
+ // Handle preflight CORS requests
111
+ if (req.method === "OPTIONS") {
112
+ res.writeHead(204);
113
+ res.end();
114
+ return;
115
+ }
116
+
117
+ // Health check endpoint
118
+ if (req.method === "GET" && req.url === "/health") {
119
+ res.writeHead(200, { "Content-Type": "application/json" });
120
+ res.end(JSON.stringify({ status: "ok" }));
121
+ return;
122
+ }
123
+
124
+ // GET all blog data
125
+ if (req.method === "GET" && req.url === "/blog") {
126
+ res.writeHead(200, { "Content-Type": "application/json" });
127
+ const dbArticles = await Article.findAll();
128
+ const responseData = {
129
+ title: blogData.title, // Keep the title from the original constant
130
+ articles: dbArticles,
131
+ };
132
+ res.end(JSON.stringify(responseData));
133
+ }
134
+ // POST a new article
135
+ else if (req.method === "POST" && req.url === "/blog/articles") {
136
+ let body = "";
137
+ req.on("data", (chunk) => (body += chunk.toString()));
138
+ req.on("end", async () => {
139
+ const newArticle = JSON.parse(body);
140
+ await Article.create(newArticle);
141
+ console.log("Added new article:", newArticle);
142
+ res.writeHead(201, { "Content-Type": "application/json" });
143
+ res.end(JSON.stringify(newArticle));
144
+ });
145
+ } else {
146
+ res.writeHead(404, { "Content-Type": "application/json" });
147
+ res.end(JSON.stringify({ message: "Not Found" }));
148
+ }
149
+ });
150
+
151
+ return new Promise((resolve) => {
152
+ server.listen(port, () => {
153
+ console.log(`API server running at http://localhost:${port}/`);
154
+ resolve(server);
155
+ });
156
+ });
157
+ } catch (error) {
158
+ console.error("Failed to initialize database or start server:", error);
159
+ process.exit(1); // Exit the process if there's a critical error
160
+ }
161
+ }
162
+
163
+ function getAPIURL() {
164
+ return `http://localhost:${serverPort}/blog`;
165
+ }
166
+
167
+ const storageserver = {
168
+ start,
169
+ getAPIURL,
170
+ setUsername,
171
+ setPassword,
172
+ setHost,
173
+ };
174
+
175
+ export default storageserver;
package/blog.db ADDED
Binary file
@@ -0,0 +1,45 @@
1
+ import globals from "globals";
2
+ import pluginJs from "@eslint/js";
3
+ import pluginJest from "eslint-plugin-jest";
4
+
5
+ export default [
6
+ {
7
+ // Configuration for all JavaScript files
8
+ files: ["**/*.js"],
9
+ languageOptions: {
10
+ ecmaVersion: 2022, // Supports modern JavaScript features
11
+ sourceType: "module",
12
+ globals: {
13
+ ...globals.node, // Defines Node.js global variables (e.g., `process`, `require`)
14
+ },
15
+ },
16
+ // ESLint's recommended rules for general JavaScript
17
+ rules: {
18
+ ...pluginJs.configs.recommended.rules,
19
+ // Add or override general JavaScript rules here.
20
+ // Example: Enforce semicolons at the end of statements
21
+ semi: ["error", "always"],
22
+ // Example: Prefer `const` over `let` where variables are not reassigned
23
+ "prefer-const": "error",
24
+ // Example: Prevent unused variables (can be configured further)
25
+ "no-unused-vars": ["warn", { args: "none" }],
26
+ },
27
+ },
28
+ {
29
+ // Configuration specifically for Jest test files
30
+ files: ["**/*.test.js", "**/*.spec.js"],
31
+ languageOptions: {
32
+ globals: {
33
+ ...globals.jest, // Defines Jest global variables (e.g., `describe`, `it`, `expect`)
34
+ },
35
+ },
36
+ plugins: {
37
+ jest: pluginJest,
38
+ },
39
+ // Recommended Jest rules from `eslint-plugin-jest`
40
+ rules: {
41
+ ...pluginJest.configs.recommended.rules,
42
+ // Add or override Jest-specific rules here.
43
+ },
44
+ },
45
+ ];
package/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import Blog from "./Blog.js";
2
2
  import Article from "./Article.js";
3
+ import storageserver from "./api-server.js";
3
4
 
4
- export { Blog, Article };
5
+ export { Blog, Article, storageserver };
package/package.json CHANGED
@@ -1,16 +1,12 @@
1
1
  {
2
2
  "name": "@lexho111/plainblog",
3
- "version": "0.0.5",
3
+ "version": "0.0.8",
4
4
  "description": "A tool for creating and serving a minimalist, single-page blog.",
5
5
  "main": "index.js",
6
6
  "type": "module",
7
- "files": [
8
- "index.js",
9
- "Blog.js",
10
- "Article.js"
11
- ],
12
7
  "scripts": {
13
- "test": "echo \"Error: no test specified\" && exit 1"
8
+ "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
9
+ "lint": "eslint ."
14
10
  },
15
11
  "keywords": [
16
12
  "blog",
@@ -18,5 +14,17 @@
18
14
  "server"
19
15
  ],
20
16
  "author": "lexho111",
21
- "license": "ISC"
17
+ "license": "ISC",
18
+ "dependencies": {
19
+ "node-fetch": "^3.3.2",
20
+ "pg": "^8.16.3",
21
+ "pg-hstore": "^2.3.4",
22
+ "sequelize": "^6.37.7",
23
+ "sqlite3": "^5.1.7"
24
+ },
25
+ "devDependencies": {
26
+ "eslint": "^9.8.0",
27
+ "eslint-plugin-jest": "^28.6.0",
28
+ "jest": "^29.7.0"
29
+ }
22
30
  }
@@ -0,0 +1,31 @@
1
+ import Article from "../Article.js";
2
+
3
+ test("get content", () => {
4
+ const article = new Article("title", "text text text");
5
+ const content = article.getContent();
6
+ expect(article.title).toContain("title");
7
+ expect(content).toContain("text text text");
8
+ });
9
+
10
+ test("get shortened content", () => {
11
+ const article = new Article("title", "text text text");
12
+ const content = article.getContentShort();
13
+ expect(article.title).toContain("title");
14
+ const text_short = "text text text";
15
+ expect(content).toContain(text_short);
16
+ expect(content).not.toContain("...");
17
+ });
18
+
19
+ test("get shortened content", () => {
20
+ let text = "text";
21
+ for (let i = 0; i < 500; i++) {
22
+ text += " ";
23
+ text += "text";
24
+ }
25
+ const text_long = text;
26
+ const article = new Article("title", text_long);
27
+ const content = article.getContentShort();
28
+ expect(article.title).toContain("title");
29
+ expect(content).toContain("text text text");
30
+ expect(content).toContain("...");
31
+ });
@@ -0,0 +1,97 @@
1
+ import Blog from "../Blog.js";
2
+ import Article from "../Article.js";
3
+ import { jest } from "@jest/globals";
4
+
5
+ describe("test blog", () => {
6
+ test("is valid html", async () => {
7
+ const myblog = new Blog();
8
+ const html = await myblog.toHTML();
9
+ expect(html).toContain("html>");
10
+ expect(html).toContain("</html>");
11
+ expect(html).toContain("<body");
12
+ expect(html).toContain("</body>");
13
+ expect(html).toContain("<div"); // container
14
+ expect(html).toContain("</div>");
15
+ });
16
+ test("creates Blog with specified title", async () => {
17
+ const titles = ["TestBlog", "Blog1234", "abcdfg", "kiwi"];
18
+ for (const title of titles) {
19
+ const myblog = new Blog();
20
+ myblog.setTitle(title);
21
+ const html = await myblog.toHTML();
22
+ expect(html).toContain(title);
23
+ }
24
+ });
25
+ test("empty blog without any article", async () => {
26
+ const myblog = new Blog();
27
+ const html = await myblog.toHTML();
28
+ expect(html).not.toContain("<article");
29
+ });
30
+ test("add article", async () => {
31
+ const myblog = new Blog();
32
+ const article = new Article("", "");
33
+ myblog.addArticle(article);
34
+ const html = await myblog.toHTML();
35
+ expect(html).toContain("<article");
36
+ expect(myblog.articles.length).toBe(1);
37
+ });
38
+ test("add articles", async () => {
39
+ const myblog = new Blog();
40
+ expect(myblog.articles.length).toBe(0);
41
+ const size = 10;
42
+ for (let i = 1; i <= size; i++) {
43
+ const article = new Article("", "");
44
+ myblog.addArticle(article);
45
+ expect(myblog.articles.length).toBe(i);
46
+ }
47
+ const html = await myblog.toHTML();
48
+ expect(html).toContain("<article");
49
+ expect(myblog.articles.length).toBe(size);
50
+ });
51
+ test("set style", async () => {
52
+ const myblog = new Blog();
53
+ const styles = [
54
+ "body { font-family: Courier; }",
55
+ "body { background-color: black; color: white; }",
56
+ "body { font-size: 1.2em; }",
57
+ ];
58
+ for (const style of styles) {
59
+ myblog.setStyle(style);
60
+ const html = await myblog.toHTML();
61
+ expect(html).toContain(style);
62
+ }
63
+ });
64
+ test("print method logs title and articles to console", () => {
65
+ // Arrange: Set up the blog with a title and articles
66
+ const myblog = new Blog();
67
+ myblog.setTitle("My Test Blog");
68
+ myblog.addArticle(new Article("Article 1", "Content 1"));
69
+
70
+ // Spy on console.log to capture its output without printing to the test runner
71
+ const consoleSpy = jest.spyOn(console, "log").mockImplementation(() => {});
72
+
73
+ // Act: Call the method we want to test
74
+ myblog.print();
75
+
76
+ // Assert: Check if console.log was called with the correct strings
77
+ expect(consoleSpy).toHaveBeenCalledWith("# My Test Blog");
78
+ expect(consoleSpy).toHaveBeenCalledWith("## Article 1");
79
+ expect(consoleSpy).toHaveBeenCalledWith("Content 1");
80
+
81
+ // Clean up the spy to restore the original console.log
82
+ consoleSpy.mockRestore();
83
+ });
84
+ });
85
+
86
+ /*
87
+ test Blog.js
88
+ ok - new Blog()
89
+ ok - set title
90
+ ok - set style
91
+ ok - add article
92
+ - set api
93
+ - fetch/post
94
+ - save/load
95
+ ok - print
96
+ ok - toHTML
97
+ */
@@ -0,0 +1,29 @@
1
+ import { start as startAPIServer } from "../api-server.js";
2
+ import fetch from "node-fetch";
3
+
4
+ describe("API Server Health Check", () => {
5
+ let server;
6
+ const PORT = 8085;
7
+ const apiBaseUrl = `http://localhost:${PORT}`;
8
+
9
+ beforeAll(async () => {
10
+ // how about to use in-memory database?
11
+ server = await startAPIServer("sqlite", PORT);
12
+ });
13
+
14
+ afterAll((done) => {
15
+ server.close(() => {
16
+ done();
17
+ });
18
+ });
19
+
20
+ test("should respond with 200 OK on the /health endpoint", async () => {
21
+ const response = await fetch(`${apiBaseUrl}/health`);
22
+
23
+ // Check for a successful status code
24
+ expect(response.status).toBe(200);
25
+
26
+ const body = await response.json();
27
+ expect(body).toEqual({ status: "ok" });
28
+ });
29
+ });