Skip to content

Commit

Permalink
Merge pull request #25 from YAPP-Github/feature/tgyuu/PC-256
Browse files Browse the repository at this point in the history
[PC-256] 이용약관 페이지 UI 구현
  • Loading branch information
tgyuuAn authored Jan 7, 2025
2 parents 804ba32 + 2d236cc commit b6a187f
Show file tree
Hide file tree
Showing 62 changed files with 1,070 additions and 226 deletions.
3 changes: 2 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,5 @@ dependencies {
implementation(libs.kakao.user)

implementation(projects.presentation)
}
implementation(projects.core.data)
}
5 changes: 4 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Piece"
Expand All @@ -32,10 +33,12 @@
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data android:host="oauth"
<data
android:host="oauth"
android:scheme="kakao${KAKAO_APP_KEY}" />
</intent-filter>
</activity>
Expand Down
17 changes: 17 additions & 0 deletions app/src/main/res/xml/network_security_config.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<debug-overrides>
<trust-anchors>
<!-- Trust user added CAs while debuggable only -->
<certificates src="user" />
<certificates src="system" />
</trust-anchors>
</debug-overrides>

<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
</trust-anchors>
</base-config>
</network-security-config>
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ internal fun Project.configureJUnitAndroid() {
unitTests.all { it.useJUnitPlatform() }
}

defaultConfig {
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

val libs = extensions.libs
dependencies {
"androidTestImplementation"(libs.findLibrary("androidx.test.ext").get())
Expand Down
1 change: 1 addition & 0 deletions core/common/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
8 changes: 8 additions & 0 deletions core/common/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
plugins {
id("piece.kotlin.library")
id("piece.kotlin.hilt")
}

dependencies {
implementation(libs.coroutines.core)
}
18 changes: 18 additions & 0 deletions core/common/src/main/java/com/puzzle/common/TimeUtil.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.puzzle.common

import java.time.LocalDateTime
import java.time.format.DateTimeParseException

/**
* String?을 LocalDateTime으로 변환합니다.
*
* - 문자열이 null인 경우, [LocalDateTime.MIN]을 반환합니다.
* - 잘못된 형식으로 인해 파싱에 실패할 경우, [LocalDateTime.MIN]을 반환합니다.
*/
fun String?.parseDateTime(): LocalDateTime {
return try {
this?.let { LocalDateTime.parse(it) } ?: LocalDateTime.MIN
} catch (e: DateTimeParseException) {
LocalDateTime.MIN
}
}
47 changes: 47 additions & 0 deletions core/common/src/test/kotlin/com/puzzle/common/TimeUtilTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.puzzle.common

import org.junit.Assert.assertEquals
import org.junit.jupiter.api.Test
import java.time.LocalDateTime

class TimeUtilTest {

@Test
fun `올바른 형식의 문자열을 LocalDateTime으로 변환할 수 있다`() {
// given
val dateTimeString = "2024-06-01T00:00:00"
val expected = LocalDateTime.parse(dateTimeString)

// when
val actual = dateTimeString.parseDateTime()

// then
assertEquals(expected, actual)
}

@Test
fun `null 값을 파싱하려고 할 경우 LocalDateTime_MIN을 반환한다`() {
// given
val nullString: String? = null
val expected = LocalDateTime.MIN

// when
val actual = nullString.parseDateTime()

// then
assertEquals(expected, actual)
}

@Test
fun `형식에 맞지 않은 문자열을 파싱하려고 할 경우 LocalDateTime_MIN을 반환한다`() {
// given
val invalidString = "invalid-date-format"
val expected = LocalDateTime.MIN

// when
val actual = invalidString.parseDateTime()

// then
assertEquals(expected, actual)
}
}
3 changes: 2 additions & 1 deletion core/data/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ android {
dependencies {
implementation(projects.core.domain)
implementation(projects.core.network)
}
implementation(projects.core.database)
}

This file was deleted.

10 changes: 9 additions & 1 deletion core/data/src/main/java/com/puzzle/data/di/DataModule.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.puzzle.data.di

import com.puzzle.data.repository.AuthRepositoryImpl
import com.puzzle.data.repository.TermsRepositoryImpl
import com.puzzle.domain.repository.AuthRepository
import com.puzzle.domain.repository.TermsRepository
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
Expand All @@ -18,4 +20,10 @@ abstract class DataModule {
abstract fun bindsAuthRepository(
authRepositoryImpl: AuthRepositoryImpl,
): AuthRepository
}

@Binds
@Singleton
abstract fun bindsTermsRepository(
termsRepositoryImpl: TermsRepositoryImpl,
): TermsRepository
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.puzzle.data.repository

import com.puzzle.database.model.terms.TermEntity
import com.puzzle.database.source.term.LocalTermDataSource
import com.puzzle.domain.model.terms.Term
import com.puzzle.domain.repository.TermsRepository
import com.puzzle.network.model.UNKNOWN_INT
import com.puzzle.network.source.TermDataSource
import javax.inject.Inject

class TermsRepositoryImpl @Inject constructor(
private val termDataSource: TermDataSource,
private val localTermDataSource: LocalTermDataSource,
) : TermsRepository {
override suspend fun loadTerms(): Result<Unit> = runCatching {
val terms = termDataSource.loadTerms()
.getOrThrow()
.toDomain()
.filter { it.termId != UNKNOWN_INT }

val termsEntity = terms.map {
TermEntity(
id = it.termId,
title = it.title,
content = it.content,
required = it.required,
startDate = it.startDate,
)
}

localTermDataSource.clearAndInsertTerms(termsEntity)
}

override suspend fun getTerms(): Result<List<Term>> = runCatching {
localTermDataSource.getTerms()
.map { it.toDomain() }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package com.puzzle.data.repository

import com.puzzle.database.source.term.LocalTermDataSource
import com.puzzle.network.model.UNKNOWN_INT
import com.puzzle.network.model.terms.LoadTermsResponse
import com.puzzle.network.model.terms.TermResponse
import com.puzzle.network.source.TermDataSource
import io.mockk.Runs
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.just
import io.mockk.mockk
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test

class TermsRepositoryImplTest {

private lateinit var termDataSource: TermDataSource
private lateinit var localTermDataSource: LocalTermDataSource
private lateinit var termsRepository: TermsRepositoryImpl

@BeforeEach
fun setUp() {
termDataSource = mockk()
localTermDataSource = mockk()
termsRepository = TermsRepositoryImpl(termDataSource, localTermDataSource)
}

@Test
fun `약관을 새로 갱신할 경우 id값이 올바르게 내려오지 않은 약관은 무시한다`() = runTest {
// given
val invalidTerm = TermResponse(
termId = UNKNOWN_INT,
title = "Invalid",
content = "Invalid Content",
required = false,
startDate = "2024-06-01T00:00:00",
)
val validTerm = TermResponse(
termId = 1,
title = "Valid",
content = "Valid Content",
required = true,
startDate = "2024-06-01T00:00:00",
)

coEvery { termDataSource.loadTerms() } returns
Result.success(LoadTermsResponse(listOf(invalidTerm, validTerm)))
coEvery { localTermDataSource.clearAndInsertTerms(any()) } just Runs

// when
val result = termsRepository.loadTerms()

// then
assertTrue(result.isSuccess)
coVerify(exactly = 1) {
localTermDataSource.clearAndInsertTerms(
match {
it.size == 1 && it.first().id == validTerm.termId
}
)
}
}

@Test
fun `갱신한 데이터는 로컬 데이터베이스에 저장한다`() = runTest {
// given
val validTerms = listOf(
TermResponse(
termId = 1,
title = "Valid1",
content = "Content1",
required = true,
startDate = "2024-06-01T00:00:00"
),
TermResponse(
termId = 2,
title = "Valid2",
content = "Content2",
required = false,
startDate = "2024-06-01T00:00:00"
)
)

coEvery { termDataSource.loadTerms() } returns Result.success(LoadTermsResponse(validTerms))
coEvery { localTermDataSource.clearAndInsertTerms(any()) } just Runs

// when
termsRepository.loadTerms()

// then
coVerify(exactly = 1) {
localTermDataSource.clearAndInsertTerms(
match {
it.size == validTerms.size && it.all { entity ->
validTerms.any { term ->
term.termId == entity.id && term.title == entity.title
}
}
}
)
}
}
}
1 change: 1 addition & 0 deletions core/database/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
16 changes: 16 additions & 0 deletions core/database/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
plugins {
id("piece.android.library")
id("piece.android.hilt")
}

android {
namespace = "com.puzzle.database"
}

dependencies {
implementation(projects.core.domain)
implementation(projects.core.common)

implementation(libs.androidx.room.ktx)
ksp(libs.androidx.room.compiler)
}
Empty file.
21 changes: 21 additions & 0 deletions core/database/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
Loading

0 comments on commit b6a187f

Please sign in to comment.